对象的初始化和清理是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用后果是未知的;
同样的,使用完一个对象和变量,没有及时清理,也会造成一定的安全问题;
C++利用了构造函数和析构函数,解决上述问题;这两个函数将会被编译器自动表用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制我们做的事情,因此如果我们不提供析构函数,编译器会执行编译器提供的构造函数和析构函数(空实现)
构造函数:主要作用是创造对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。
析构函数:主要作用于对象销毁前,系统自动调用,执行一些清理工作。
构造函数语法:类名(){}
1.构造函数,没有返回值,也不写void2.函数名称与类名相同3.构造函数可以有参数,因此可以发生重载4.程序在调用对象会自动调用构造,无需手动调用,而且只会调用一次;析构函数:~类名(){}
1.析构函数,没有返回值也不写void2.函数名称和类名相同,在名称前加上符号~3.析构函数不可以有参数,因此不可以发生重载;4.程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次; //1.构造函数 class Person01 { public: Person01() { cout << "构造函数的调用" << endl; } //2.析构函数 //析构函数不可以有参数,不可以发生重载 //在销毁前会自动的调用析构函数,且只调用一次 ~Person01() { cout << "析构函数的调用" << endl; } }; void test01() { Person01 p; } int main() { //test01(); system("pause"); return 0; }两种分类方式:
按参数分为:有参构造、无参构造;
按照调用分为: 括号法、显示法、隐式函数转换法
注意事项1 调用构造默认函数时候,不要+();
注意事项2: 不用利用拷贝构造函数初始化匿名对象
class Person01 { public: Person01() { cout << "Person01 无参构造函数的调用" << endl; } Person01(int a) { age = a; cout << "Person01 有参构造函数的调用" << endl; } //拷贝构造函数 Person01(const Person01 &p) { //将传入的人身上的所有参数都copy过来 age = p.age; cout << "Person01copy 构造函数的调用" << endl; } //2.析构函数 ~Person01() { cout << "Person01 析构函数的调用" << endl; } int age; }; void test01() { // 1 括号法 //Person01 p; //无参函数构造 //Person01 p2(10);//有参函数构造; //拷贝构造函数 //Person01 p3(p2); /*cout << "p2 的年龄 = " << p2.age << endl; cout << "p3 的年龄 = " << p3.age << endl;*/ //注意事项1 调用构造默认函数时候,不要+(); //因为下面这行代码,编译器会认为是一个函数的声明,不会认为在创建对象 //Person01 p1(); //2、显示法 //注意事项二: 不用利用拷贝构造函数初始化匿名对象 Person01 p1; Person01 p2 = Person01(10); //有参构造 Person01 p3 = Person01(p2); //拷贝构造 //Person01(p3);//编译器会认为Person01(p3) = Person01 p3;对象重定义 //Person01(10);//匿名对象 特点,当前行执行结束之后,系统会立即回收匿名对象; //隐式转换法 Person01 p4 = 10; // Person01 p4(10) || Person01 p4 = Person01(10); } int main() { test01(); system("pause"); return 0; }C++中拷贝构造函数调用时机通常有三种
使用一个以及创建完成的对象来初始化一个对象值传递的方式给函数参数传递以值方式返回局部对象 //1.使用一个已经创建完毕的对象来初始化一个新对象 void test02() { Person02 p1(20); Person02 p2(p1); } //2.值传递的方式给函数参数传值 void doWork(Person02 p) { } void test03() { Person02 p; doWork(p); } //3.值方式返回局部变量 Person02 doWork01() { Person02 p1; cout << (int*)& p1 << endl; return p1; } void test04() { Person02 p = doWork01(); cout << (int*)& p << endl; } int main() { test04(); system("pause"); return 0; }默认情况下,C++编译器至少给一个类添加三个函数
默认构造函数(空实现)析构函数(空实现)拷贝构造函数(值拷贝)构造函数调用规则:
如果用户定义了有参构造函数,C++不会提供无参构造函数,但是会提供拷贝构造函数如果用户定义了拷贝构造函数,C++不会再提供其他构造函数;浅拷贝:简单的赋值拷贝操作 ;浅拷贝带来的问题是堆区内存的重复释放;
深拷贝:在堆区重新申请空间进行拷贝操作;
class Person02 { public: Person02() { cout << "Person 无参构造函数" << endl; } Person02(int age ,int height) { m_Age = age; m_Hight = new int(height); cout << "Person 有参构造函数" << endl; } Person02(const Person02 &a) { m_Age = a.m_Age; m_Hight = new int(*a.m_Hight); cout << "Person 拷贝参构造函数" << endl; } ~Person02() { if (m_Hight != NULL) { delete m_Hight; } cout << "Person02析构函数的调用" << endl; } int m_Age; int* m_Hight; }; void test05() { Person02 p1(18,160); cout << "p1.age =" << p1.m_Age << endl; cout << "p1.m_Height =" << *p1.m_Hight << endl; Person02 p2(p1); cout << "p2.age =" << p2.m_Age << endl; cout << "p2.m_Height =" << *p2.m_Hight << endl; } int main() { test05(); system("pause"); return 0; }总结:如果属性有在堆区开辟的内存,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题;
作用:
C++提供了初始化列表语法,用来初始化属性;
语法: 构造函数():属性1(值),属性2(值)...{}
class Person03 { public: //传统赋值操作 //Person03(int a, int b, int c) //{ // m_A = a; // m_B = b; // m_C = c; //} //初始化列表 Person03(int a,int b,int c) : m_A(a), m_B(b), m_C(c) { } int m_A; int m_B; int m_C; }; void test06() { Person03 p(30,20,10); cout << "m_A 的值" << p.m_A << endl; cout << "m_B 的值" << p.m_B << endl; cout << "m_C 的值" << p.m_C << endl; } int main() { test06(); system("pause"); return 0; }C++类中的成员可以是另一个类的对象,我们称之为对象成员;
例如B类中有 对象A 作为成员,A为对象成员;那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后呢?
先构造A后构造B;
当其他类的对象作为本类的成员,当构造的时候需要先构造成员类对象,再构造类本身;析构的顺序与之相反;、
//类对象作为类成员 class Phone { public: Phone(string name) { cout << "Phone 构造函数" << endl; m_PName = name; } string m_PName; }; class Person04 { public: Person04(string name, string pName) : m_Name(name),m_Phone(pName) { cout << " Person04 构造函数" << endl; } string m_Name; Phone m_Phone; }; int main() { Person04 p("zhangsan ", "iphone"); cout << p.m_Name << " 拿着 " << p.m_Phone.m_PName << endl; system("pause"); return 0; }静态成员就是在成员变量和成员函数前加上关键字static ,称之为静态成员;
静态成员分为:
静态成员变量 所有对象共享一份数据 在编译阶段分配内存 类内声明,类外初始化 静态成员函数 所有对象共享一个函数 静态成员函数只能访问静态成员变量 //静态成员函数 //所有对象共享一个函数 //静态成员函数只能访问静态成员变量 class person { public: static void func() { m_A = 100;//静态成员函数无法访问非静态成员变量,因为无法区别那个对象的变量 cout << "static void func " << endl; } static int m_A; int m_B; //静态成员函数也是有访问权限的 private: static void func2() { } }; //两种方式 void test07() { //通过对象访问 person p; p.func(); //通过类名访问,不需要对象 person::func(); } int main() { test07(); system("pause"); return 0; }