C++之基础知识20170830

/******************************************************************************************************************/

一、C++类的引入

与C相比,

1.编译使用g++代替 gcc,执行在linux中还是一样的

2.c++里面也有struct并对其进行了扩展,struct中的函数可以直接使用其成员,并可在struct中直接实现,

例:

struct person {

         char *name;

         int age;

         char *work;

         void printInfo(void)

         {

                   printf("name = %s, age = %d, work = %s ", name, age, work);

         }

};

int main(int argc, char **argv)

{

         struct person persons[] = {

                   {"zhangsan", 10, "teacher"},

                   {"lisi", 16, "doctor"},

         };

         persons[0].printInfo();

         persons[1].printInfo();

         return 0;

}

3.这样的struct可以使用class代替,注意权限问题,class还有新的特性,

例:

class person {

public:

//注意权限问题

         char *name;

         int age;

         char *work;

         void printInfo(void)

         {

                   printf("name = %s, age = %d, work = %s ", name, age, work);

         }

};

/******************************************************************************************************************/

二、C++基础知识_访问控制

C++有三种访问权限private,protected,public

1.默认权限是private

2.对象的创建

类 对象

例:Person mPer;

3.访问本类中的属性,使用this->

由于c和c++里面有就近原则,所以这样就可以使用同名

访问父类的属性,直接使用该属性名即可(或者使用 父类名::属性名 的方式来访问)

/******************************************************************************************************************/

三、C++基础知识_程序结构

1.类中的成员函数在类中可不定义只是声明,实现放在类外面(类名 :: 函数名)

例:

class Person {

private:

         char *name;

         int age;

         char *work;

public:

         void setName(char *name);

         int setAge(int age);

         void printInfo(void);

};

void Person::setName(char *name)

{

         this->name = name;

}

int Person::setAge(int age)

{

         if (age < 0 || age > 150)

         {

                   this->age = 0;

                   return -1;

         }

         this->age = age;

         return 0;

}

void Person::printInfo(void)

{

         printf("name = %s, age = %d, work = %s ", name, age, work);

}

int main(int argc, char **argv)

{

         Person per;

         //per.name = "zhangsan";

         per.setName("zhangsan");

         per.setAge(200);

         per.printInfo();

         return 0;

}

2. 实现放在类外面,这样就可以优化整个程序结构,

把类的定义(函数成员只是声明)放在头文件中

使用者包含这个头文件,就知道怎么用了(不去关心具体实现)

具体实现放在具体实现文件里,包含对应头文件。

Linux下的编译方式

person: main.o person.o

         g++ -o $@ $^     //$@表示person(目标),$^表示所有的依赖

%.o : %.cpp

         g++ -c -o $@ $<    //$<表示第一个依赖

3.为防止出现函数名一致(函数名重复),导致不知道具体使用哪一个函数,引入命名空间namespace,

1).用法:

在定义和声明的地方,用namespace包含起来,例

namespace A

{

}

2).调用的时候使用A::函数名

注意使用A中的类创建对象也要加上A,即A::Person per;

per.setName();

3).如果命名空间之间的类名没有冲突,

类创建对象是可以不使用A::Person per;

使用using声明,即using A::Person;(一般放在文件前面)

文件前面代表的是全局命名空间,所以

/* 把A::Person放入global namespace, 以后可以使用Person来表示A::Person */

4).如果命名空间之间的类名没有冲突,

也可以使用using编译,即using namespace A;(一般放在文件前面)

把A空间里面所有的内容都导进来

出现冲突的地方必须指明函数所在的命名空间(A::函数名)

4.C++打印使用cout<<endl; 代替printf,

例:

std::cout<<"name = "<<name<<" age = "<<age<<" work = "<<work<<std::endl;

注意

1).需包含头文件

#include <iostream>

2).由于使用了命名空间,要指明cout的命名空间为标准命名空间里的

std::cout<<"name = "<<name<<" age = "<<age<<" work = "<<work<<std::endl;

或在文件前面指明using namespace std;

/******************************************************************************************************************/

四、C++基础知识_重载_指针_引用

1.重载

函数名一样,参数不一样

2.指针

与C语言一样

3.引用(类似内存地址,使用和变量名一样)

C++引入引用就是为了避免使用指针(防止指针使用不恰当),其实就是操作同一块内存

例:

int a=100;

int &b=a;//b就是a的引用

b是a的别名,操作b也就是操作a

定义:

int add_one_ref(int &b)

{

b = b+1;

return b;

}

使用:cout<<add_one_ref(a)<<endl;

注意,引用定义的时候就必须初始化,即赋值,同时引用是变量的别名,所以只能使用变量来赋值

/******************************************************************************************************************/

五、C++基础知识_构造函数

1.构造函数:在类中,和类名名字相同的函数,可以带参数也可不带

1).带参数的,创建对象时可传入,

Person per("zhangsan", 16);

2).无传入参数的,

Person per2;

//Person per2();//不使用这个,这个表示的是函数名为per2的函数的声明

2.不想写很多的重载的构造函数的情况

Person(char *name, int age, char *work = "none")

{//表示如果没有传入第三个参数,则第三个参数为"none"

cout <<"Pserson(char*, int)"<<endl;

this->name = name;

this->age = age;

this->work = work;

}

Person per("zhangsan", 16);//导致this->work ="none"

3.除Person per("zhangsan", 16);方式外,创建对象的方式

使用指针的方式:

Person *per4 = new Person;

//这两种创建对象的方式一样,都是调用无参构造函数

Person *per5 = new Person();

//这两种创建对象的方式一样,都是调用无参构造函数

Person *per6 = new Person[2];

//per6是个对象数组,数组成员是对象     

Person *per7 = new Person("lisi", 18, "student");

Person *per8 = new Person("wangwu", 18);

per.printInfo();

per7->printInfo();

per8->printInfo();

//动态创建的对象(new出来的对象),整个程序(进程)执行完后会自动释放所有对象占用的内存空间(包括子函数中创建的对象),如果要手动释放,可以用delete命令:  

delete per4;

delete per5;

delete []per6;

//对象数组的删除

delete per7;

delete per8;

//不是new的对象,局部变量:函数执行完会自动销毁,全局变量:一直存在

4.分配堆空间,使用new代替malloc

分配:

this->name = new char[strlen(name) + 1];

strcpy(this->name, name);

释放:

delete this->name;

linux中,free命令查看空闲内存

5.析构函数(只能有一个)

释放对象时,对象所占据的内存释放了,如果对象的函数成员中又new了对象,同时又没有对函数成员中new的对象delete,那么就会造成内存泄漏。

比较方便解决这个问题,就是引入析构函数,析构函数中加入对函数成员中new的对象的delete操作,这样delete对象时就释放了函数成员中new的对象(delete销毁对象的同时就会调用析构函数)

例:

~Person()

{

if (this->name)

delete this->name;

if (this->work)

delete this->work;

}

注意使用new创建的对象,用完要用delete释放

6.默认拷贝构造函数

拷贝构造函数和赋值函数的区别:拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。

String a("A");

String b("B");

String c = a; // 调用了拷贝构造函数,最好写成c(a);

c = b; // 调用了赋值函数

 

拷贝构造函数分为深拷贝和浅拷贝,深浅拷贝:https://blog.csdn.net/libin66/article/details/53140284

无参数时,默认构造函数

有对象作为参数时,默认拷贝构造函数(进行值的拷贝,即浅拷贝) //C++类有四个默认函数---构造函数、拷贝构造函数(浅拷贝)、析构函数、赋值函数(浅拷贝):https://blog.csdn.net/anye3000/article/details/6570445)

Person per("zhangsan", 18);

Person per2(per);// 有对象参数, per是对象

默认拷贝构造函数,会改变其中的值,则在释放的时候,同一个值对应的堆空间会被释放两次,所以要定义自己的拷贝构造函数

Person(Person &per)

{

cout <<"Pserson(Person &)"<<endl;

this->age = per.age;

this->name = new char[strlen(per.name) + 1];//重新分配空间,如果是默认拷贝构造函数,就会进行值的拷贝,即这个指针的值等于传进来对象成员指针的值,导致不同对象指向同一个空间,从而导致内存释放时会释放两次

strcpy(this->name, per.name);

this->work = new char[strlen(per.work) + 1];

strcpy(this->work, per.work);

}

7.对象的构造顺序

1).创建对象首先是全局变量(在main函数执行之前),然后碰到哪个就创建哪个(不管是否静态),即按运行中定义对象的顺序调用构造函数。

由于静态对象的生存周期,退出函数不会被销毁,同时再次进入函数由于已经创建了也不会再次创建,即只定义一次

2).类中的数据成员是对象的情况,

I、先调用成员对象的构造函数,再调用自己的构造函数。

调用成员对象的无参构造函数,无需添加什么会自动调用

II、调用成员对象(数据成员是对象)的有参构造函数的方法

在自己类的构造函数后面加上:号,例:

class Student {

private:

Person father;//定义顺序

Person mother;

int student_id;

public:

Student()

{

cout<<"Student()"<<endl;

}

Student(int id, char *father, char *mother, int father_age = 40, int mother_age = 39) : mother(mother, mother_age), father(father, father_age)

{//加上:号就会去调用father对象的有参构造函数,修改:号后面的对象顺序不影响构造顺序,构造顺序按照定义的顺序,即先father,再mother,最后自己的         

        

cout<<"Student(int id, char *father, char *mother, int father_age = 40, int mother_age = 39)"<<endl;

}

~Student()

{

cout<<"~Student()"<<endl;

}

};

III、析构函数的调用顺序

和构造函数的调用顺序相反

/******************************************************************************************************************/

六、C++基础知识_静态成员_友员

静态成员是属于类的(放在类的类名空间中),只占一份空间,不属于对象

1.类中定义

private:

static int cnt; //静态成员

public:

static int getCount(void)

{ //静态成员用于访问静态成员变量

//静态成员函数不能访问非静态成员变量(还没定义分配)    

                  return cnt;

}

2.使用

一般使用成员函数访问:Person::getCount()

如果是公有属性,也可以Person::cnt;

3.class是struct的进化,所以还需要分配空间

int Person::cnt = 0; /* 定义和初始化 不再需要加static*/

//同时要放在main之前(放在main内会编译出错),因为要在创建对象之前就定义和初始化

4.也可以在类外定义静态成员函数

public:

static int getCount(void);//类内部声明

int Person::getCount(void)

{ //类外部定义,不再需要加static     

return cnt;

}

5.也可以使用类的实例化对象调用静态成员函数

cout << "person number = "<<Person::getCount()<<endl;

cout << "person number = "<<p[0].getCount()<<endl;//类的实例化对象调用静态成员函数

cout << "person number = "<<p[1].getCount()<<endl;

6.友元

1.Point(int x, int y) : x(x), y(y) {}

等价于

Point(int x, int y){this->x=x,this->y=y}

2.类的友元函数(该函数一般会操作该类)可以直接访问该类的私有成员

1)首先在类中声明是友元函数

class Point

{

private:

int x;

         int y;

public:

Point() {}

Point(int x, int y) : x(x), y(y) {}

int getX(){ return x; }

int getY(){ return y; }

void setX(int x){ this->x = x; }

void setY(int y){ this->y = y; }

void printInfo()

{

cout<<"("<<x<<", "<<y<<")"<<endl;

}

friend Point add(Point &p1, Point &p2);

};

2)友元函数就可以直接使用类的私有成员了

Point add(Point &p1, Point &p2)

{

Point n;

n.x = p1.x+p2.x;

         n.y = p1.y+p2.y;

return n;

}

/******************************************************************************************************************/

七、C++基础知识_运算符重载_类外函数

1.加减乘除运算的重载

例如+号的重载

1).定义:

Point operator+(Point &p1, Point &p2)

{

cout<<"Point operator+(Point &p1, Point &p2)"<<endl;

Point n;

n.x = p1.x+p2.x;

n.y = p1.y+p2.y;

return n;

}

2).使用

Point p1(1, 2);

Point p2(2, 3);

Point sum = p1+p2;

2.++p,p++的重载

1).定义:

/* Point p(1,2); ++p; */

Point operator++(Point &p)

{

cout<<"++p"<<endl;

p.x += 1;

p.y += 1;

return p;

}

/* Point p(1,2); p++; */

Point operator++(Point &p, int a)

{//参数不同实现重载

cout<<"p++"<<endl;

Point n;

n = p;

p.x += 1;

p.y += 1;

return n; 

}

2).使用

Point p1(1, 2);

Point p2(2, 3);

Point n = ++p1;

n.printInfo();

p1.printInfo();

cout << "******************"<<endl;

Point m = p2++;

m.printInfo();

p2.printInfo();

4.返回值是对象时,返回时会创建临时对象的问题(临时对象是局部变量用完又会被销毁)

/* Point p(1,2); ++p; */

Point operator++(Point &p)

{

cout<<"++p"<<endl;

p.x += 1;

p.y += 1;

return p;//返回引用,返回类型又是对象,系统就会自动创建临时对象,用完销毁(整个过程会调用构造函数与析构函数,浪费时间)

}

改进:

/* Point p(1,2); ++p; */

Point& operator++(Point &p)

{//使用引用则不会再去创建

cout<<"++p"<<endl;

p.x += 1;

p.y += 1;

return p;//使用引用则不会再去创建

}

Point operator++(Point &p, int a)

{//参数不同实现重载

cout<<"p++"<<endl; //endl表示换行

Point n;

n = p;

p.x += 1;

p.y += 1;

return n;//已经创建了n(已经有临时变量了)则不会再去创建临时对象 

}

5.使用返回对象还是引用,取决于是否影响程序执行结果

引用返回效率高,在不影响运算结果则效率优先,即使用引用

6.<<的重载(<<也是运算符)

1)定义

ostream& operator<<(ostream &o, Point p)

{

cout<<"("<<p.x<<", "<<p.y<<")";//不需要换行

return o;

}

2)使用

cout<<m<<" "<<n<<endl;

//<<m表示执行<<m,并将结果输出到ostream

//<<m<<n表示把先执行<<m得到的ostream与n一起作为<<的参数,从而得到新的ostream引用,即<<n执行的结果追加到之前执行的<<m的结果后面

//cout<<m<<" "<<n<<endl,表示cout使用 执行<<m<<" "<<n<<endl的返回值,即<<m<<" "<<n<<endl的返回值输出到cout

7.运算符重载函数可直接调用

operator++(p1,0);//后一个参数随便传入值,只是为了分辨

operator<<(cout,p1);

/******************************************************************************************************************/

八、C++基础知识_运算符重载_成员函数

1.类中定义重载函数

Point operator+(Point &p)

{

cout<<"operator+"<<endl;

Point n;

n.x = this->x + p.x; //类中所以可以这么用 p.x

n.y = this->y + p.y;

return n;

}

/* Point p(1,2); ++p; */

Point& operator++(void)

{

cout<<"operator++(void)"<<endl;

this->x += 1;

this->y += 1;

return *this; //返回别名,变量名

}

/* Point p(1,2); p++; */

Point operator++(int a)

{

cout<<"operator++(int a)"<<endl;

Point n;

n = *this;

this->x += 1;

this->y += 1;

return n; 

}

2.使用

Point p1(1, 2);

Point p2(2, 3);

Point m, n;

m = p1 + p2; /* m = p1.operator+(p2); */

cout<<"add p1,p2 = "<<m<<endl;

cout<<"begin"<<endl;

m = ++p1;    /* m = p1.operator++(); */

cout<<"m = "<<m<<" "<<"p1 = "<<p1<<endl;

cout << "******************"<<endl;

n = p1++;    /* m = p1.operator++(0); */

cout<<"n = "<<n<<" "<<"p1 = "<<p1<<endl;

cout<<"end"<<endl;

3.重载等号

对象拷贝时,可能会导致两个对象中的指针指向同一块内存,就有可能导致同一块内存被释放两次(或者覆盖导致,导致原有的内存泄漏)

重载等号可解决这个问题

1.类中定义

Person& operator=(const Person& p)

{

cout << "operator=(const Person& p)"<<endl;

if (this == &p)

return *this;

this->age = p.age;

if (this->name)

{

delete this->name;

}

if (this->work)

{

delete this->work;

}

this->name = new char[strlen(p.name) + 1];

strcpy(this->name, p.name);

this->work = new char[strlen(p.work) + 1];

strcpy(this->work, p.work);

return *this;

}

2.使用

const Person p1("zhangsan", 10);

cout<<"Person p2 = p1" <<endl;

Person p2 = p1;

//调用拷贝构造函数,没有调用重载等号函数

//注意拷贝构造函数的参数要加上const,来兼容只读的const参数(const对象作为参数)const对象只能调用const函数(函数声明后加上const,表示这个函数不会有修改的操作,不会修改类中的变量)     

Person p3;

cout<<"p3=p1"<<endl;

p3 = p1;//调用重载等号函数

cout<<"end"<<endl;

//p3=(p2=p) 再次赋值的情况,所以重载函数需要返回值

原文地址:https://www.cnblogs.com/yuweifeng/p/7455036.html