目录
构造函数是一个特殊的成员函数,名字与类名相同,创建类对象时由编译器自动调用,并且在对象的整个生命周期内只调用一次
1).构造函数,并不开辟空间创建对象,而是对已经创建的对象初始化
2).函数名与类名相同
3).无返回值
4).对象实例化时编译器自动调用
5).构造函数支持重载
6).对内置类型(char,int,double...)不做处理,对自定义类型,自动调用自定义类型的构造函数
- class Date
- {
- public:
-
- //1.无参构造函数(如果不写构造函数,则系统默认生成)
- Date()
- {
-
- }
- //2.有参构造函数(与无参构造函数发生重载)
- Date(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- //3.带缺省参数的有参构造
- //Date(int year, int month = 3, int day = 10)
- //{
- // _year = year;
- // _month = month;
- // _day = day;
- //}
-
- private:
- int _year;
- int _month;
- int _day;
- };
-
- int main()
- {
- Date d1;//调用无参构造 注意:不要写成Date d1()这样会被当作函数声明
- Date d2(2022, 8, 7);//调用有参构造
-
- return 0;
- }
一般把默认构造函数分为三类:
编译器自动生成的无参构造函数
全缺省的有参构造函数
我们自己写的无参构造函数
默认构造函数只能有一个,本质原因就是如果有多个的话,无法发生重载
代码:

所以当我们没有写构造函数的时候,编译器自动生成的构造函数有什么用呢?
作用就在于,编译器默认生成的是构造函数,只要是构造函数,如果有自定义类型,就会对自定义类型调用其自己的默认构造函数。
构造函数对内置类型不做处理,我们可以看到调用编译器生成的构造时,仍然还是随机值
C++11中对于构造函数内置类型不初始化这一缺陷打了一个补丁:
内置类型成员在声明时,可以以缺省值的方式给默认值
- class Date
- {
- public:
-
- private:
- //这里用给缺省值的方式,来给内置类型设置默认值,注意这里仍然是声明
- int _year = 2022;
- int _month = 8;
- int _day = 7;
- };
- int main()
- {
- Date d1;
- return 0;
- }

与构造函数功能相反,构造函数完成了对刚创建的对象的初始化,析构函数完成了对即将销毁的对象的数据释放
析构函数不是完成对对象本身的销毁,对象销毁与对象创建一样,由编译器完成,对象在销毁时自动调用析构函数,完成对象中的资源清理
1).析构函数名是在类名之前加上~
2).无参无返回值
3).一个类只能有一个析构函数
4).若没有显式定义则系统自动生成默认析构函数
5).对象生命周期结束时,编译器自动调用析构
6).如果有自定义类型,该类析构函数会自动调用自定义类型中的析构函数
- class Time
- {
- public:
- Time()
- {
- _hour = 1;
- _minute = 1;
- _second = 1;
- cout << "调用Time构造函数" << endl;
- }
- ~Time()
- {
- cout << "调用Time析构函数" << endl;
- }
- private:
- int _hour;
- int _minute;
- int _second;
- };
- class Date
- {
- public:
- Date()
- {
- _year = 2022;
- _month = 8;
- _day = 7;
- cout << "调用Date构造函数" << endl;
- }
- ~Date()
- {
- cout << "调用Date析构函数" << endl;
- }
- private:
- int _year;
- int _month;
- int _day;
- Time _t;
- };
-
- int main()
- {
- Date d1;
- return 0;
- }

构造顺序:首先编译器创建出对象d1,在给d1初始化时,需要先调用对象t的构造,t初始化好之后,再调用Date构造
析构顺序:编译器不会直接去调用Time析构,而是先调用Date析构,Date析构在清理数据时发现,还需要清理对象t,然后接着调用Time析构
如果没有在堆区创建的对象,一般直接使用默认生成的就ok了,如果有需要释放的数据空间则一定要显示去写析构函数
此外,还要记住一点,后构造的先析构
拷贝构造是构造函数的一种,在对象创建时,将一个对象完整的拷贝给另一个对象
拷贝构造函数是对一个对象的初始化,而不是赋值!
1).拷贝构造函数是构造函数的一个重载形式
2).参数只有一个且必须是类类型对象的引用,如果以传值的方式将直接报错
3).如果没有显示定义,编译器会自动生成默认的拷贝构造函数(浅拷贝)
参数必须是类类型对象的引用
- //因为仅仅只是拷贝,而又必须使用传引用的方式
- //防止因误操作将原先数据修改,一般在前面加上const
- Date(const Date& d)
- {
- *this = d;
- }
如果直接传值可不可以呢,例如Date(const Date d),这样不行,会发生无限递归调用拷贝构造
~~如果是传值
因为要调用拷贝构造,但每次传值的本质就是拷贝(编译器新创建一个变量然后需要调用拷贝构造来初始化这个变量)就又需要调用拷贝构造,这样一直循环下去
~~如果是传引用
传引用的本质就是传地址,传地址不需要调用拷贝构造
编译器自动生成的拷贝构造是一种浅拷贝
对于内置类型按照字节直接拷贝(浅拷贝)对于自定义类型调用其自己的拷贝构造
当没有涉及到资源申请的时候,拷贝构造是否显式去写都可以,一旦涉及到malloc或者new,那么拷贝构造是一定要写的,如果不写,就用编译器生成的那一定是浅拷贝,本来1个指针指向一块空间,拷贝之后并没有拷贝空间而是直接拷贝了一个指针,2个指针指向同一个空间,很容易出问题
1).用类实例化对象,且用已有对象初始化
2).函数传参类型为类的类型
3).函数返回值为类的类型