- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
#include <iostream> using namespace std; //对象初始化和清理 class Person { public: //1、构造函数 进行初始化操作 //构造函数 //没有返回值 不用写void //函数名 与类名相同 //构造函数可以有参数 可以发生重载 //创建对象的时候,构造函数会自动调用,而且只调用一次 Person() { cout << "Person 构造函数的调用" << endl; } //2、析构函数 进行清理操作 //没有返回值 不写void //函数名和类名相同 在名称前加~ //析构函数不可以有参数的,不可以发生重载 //对象在销毁前 会自动调用析构函数,而且只会调用一次 ~Person() { cout << "Person 的析构函数调用" << endl; } }; void test01() { Person p;//在栈上的数据,test01执行完毕后,释放这个对象 } int main() { //test01(); Person p; system("pause"); return 0; }
1、构造函数
构造函数语法: 类名(){}
1.造函数,没有返回值也不写yoid
2.函数名称与类名相同
3.构造函数可以有参数,因此可以发生重载
4.程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次构造函数的分类及调用
#include <iostream> using namespace std; //1、构造函数的分类及调用 //分类 //按照参数分类 无参构造(默认构造) 有参构造 //按照类型分类 普通构造 拷贝构造 class Person { public: //构造函数 Person() { cout << "Person 的无参构造函数调用" << endl; } Person(int a) { age = a; cout << "Person 的有参构造函数调用" << endl; } //拷贝构造函数 Person(const Person &p) { //将传入的人身上的所有属性,拷贝到我身上 cout << "Person 的拷贝构造函数调用" << endl; age = p.age; } ~Person() { cout << "Person 的析构函数调用" << endl; } int age; }; //调用 void test01() { //1、括号法 //Person p1;//默认构造函数调用 //Person p2(10);//有参构造函数调用 //Person p3(p2);//拷贝构造函数调用 //cout << "p2的年龄为:" << p2.age << endl; //cout << "p3的年龄为:" << p3.age << endl; //注意事项1 //调用默认构造函数时候,不要加() //因为下面这行代码,编译器认为是一个函数的声明,不会认为在创建对象 //Person p1(); //2、显示法 //Person p1; //Person p2 = Person(10);//有参构造 //Person p3 = Person(p2);//拷贝构造 //Person(10);//匿名对象 特点:当前执行结束后,系统会立即回收匿名对象 //cout << "aaaa" << endl; //注意事项2 //不要利用拷贝构造函数 初始化匿名对象 编译器会认为Person(p3)=== Person p3;对象声明 /*Person(p3);*/ //3、隐式转换法 Person p4 = 10;//相当于写了 Person p4=Person(10); 有参构造、 Person p5 = p4;//拷贝构造 } int main() { test01(); system("pause"); return 0; }
拷贝构造函数调用时机
C++中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
#include <iostream> using namespace std; class Person { public: Person() { cout << "Person默认构造函数调用" << endl;; } Person(int age) { cout << "Person有参构造函数调用" << endl;; m_Age = age; } Person(const Person &p) { cout << "Person拷贝构造函数调用" << endl;; m_Age = p.m_Age; } ~Person() { cout << "Person析构函数调用"<<endl; } int m_Age; }; //1、使用一个已经创建完毕的对象来初始化一个新对象 void test01() { Person p1(10); Person p2(p1); } //2、值传递的方式给函数参数传值 void doWork(Person p) { } void test02() { Person p; doWork(p); } //3、以值方式返回局部对象 Person doWork2() { Person p1; return p1; } void test03() { Person p = doWork2(); } int main() { //test01(); //test02(); test03(); system("pause"); return 0; }
构造函数调用规则
默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝构造函数调用规则如下:
- 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
#include <iostream> using namespace std; class Person { public: /*Person() { cout << "Person默认构造函数调用" << endl;; }*/ Person(int age) { cout << "Person有参构造函数调用" << endl;; m_Age = age; } /*Person(const Person &p) { cout << "Person拷贝构造函数调用" << endl;; m_Age = p.m_Age; }*/ ~Person() { cout << "Person析构函数调用"<<endl; } int m_Age; }; //void test01() { // Person p; // p.m_Age = 18; // // Person p2(p); // cout << "p2的年龄为:" << p.m_Age << endl; //} void test02() { Person p(28); Person p2(p); cout << "p2的年龄为:" << p.m_Age << endl; } int main() { //test01(); test02(); system("pause"); return 0; }
深拷贝与浅拷贝
浅拷贝: 简单的赋值拷贝操作
深拷贝: 在堆区重新申请空间,进行拷贝操作#include <iostream> using namespace std; class Person { public: Person() { cout << "Person默认构造函数调用" << endl;; } Person(int age,int height) { m_Age = age; m_Height = new int(height); cout << "Person有参构造函数调用" << endl;; } //自己实现拷贝构造函数 解决浅拷贝带来的问题 堆区二次重复释放 Person(const Person& p) { cout << "Person 拷贝构造函数的低调用" << endl; m_Age = p.m_Age;//编译器默认实现这行代码 m_Height = p.m_Height;//编译器默认实现这行代码,所有导致堆区重复释放 //用深拷贝解决上述问题 m_Height=new int(*p.m_Height); } ~Person() { //析构代码,将堆区开辟数据做释放操作 if (m_Height != NULL) { delete m_Height; m_Height = NULL; } cout << "Person析构函数调用"<<endl; } int m_Age; int *m_Height; }; void test01() { Person p1(18,160); cout << "p1的年龄为:"<<p1.m_Age <<" 身高为:"<<*p1.m_Height<< endl; Person p2(p1); cout << "p2的年龄为:" << p2.m_Age << " 身高为:" << *p2.m_Height << endl; } int main() { test01(); system("pause"); return 0; }
总结: 如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
初始化列表
#include <iostream> using namespace std; class Person { public: //传统初始化操作 //Person(int a, int b, int c) { // m_A = a; // m_B = b; // m_C = c; //} //初始化列表初始化属性 /*Person() :m_A(10), m_B(20), m_C(30) { }*/ Person(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 test01() { //Person p(10, 20, 30); //Person p; Person 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() { test01(); system("pause"); return 0; }
类对象作为类成员
#include <iostream> using namespace std; #include <string> //手机类 class Phone { public: Phone(string pName) { cout << "Phone构造函数的调用" << endl; m_PName = pName; } ~Phone() { cout << "Phoen析构函数调用" << endl; } string m_PName; }; //人类 class Person { public: Person(string name, string pName) :m_Name(name),m_Phone(pName){ cout << "Person的构造函数调用" << endl; } ~Person() { cout << "Person的析构函数调用" << endl; } //姓名 string m_Name; //手机 Phone m_Phone; }; //当其他类对象作为本类成员,构造时候先构造类对象,再构造自身 ,析构顺序与构造相反 void test01() { Person p("张三", "苹果MAX"); cout << p.m_Name << "拿着" << p.m_Phone.m_PName << endl; } int main() { test01(); system("pause"); return 0; }
2、析构函数
析构函数语法:~类名(){}
1.析构函数,没有返回值也不写void
2.函数名称与类名相同,在名称前加上符号~
3.析构函数不可以有参数,因此不可以发生重载
4.程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次