构造函数和析构函数,这两个函数将会被编译器自动调用,构造函数完成对象的初始化动作,析构函数在对象结束的时候完成清理工作。
注意:对象的初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事。
构造函数:实例化对象的时候系统自动调用
析构函数:对象释放的时候系统自动调用
构造和析构函数定义构造函数语法:
构造函数函数名和类名相同,没有返回类型,连void都不可以,但可以有参数,可以重载
析构函数语法:
析构函数函数名是在类名前面加”~”组成,没有返回类型,连void都不可以,不能有参数,不能重载
class Data
{
public:
int num;
public:
//构造函数(无参的构造)
Data()
{
num = 0;
cout<<"无参的构造函数"<
构造函数的分类以及调用
1、构造函数分类:
按参数类型:分为无参构造函数和有参构造函数
按类型分类:普通构造函数和拷贝构造函数(复制构造函数)
2、构造函数的调用规则:
1)编译器会给一个类,至少添加三个函数 默认构造(空实现)、析构函数(空实现)、拷贝构造(值拷贝)
2)如果我们自己提供了 有参构造函数,编译器就不会提供默认构造函数,但是依旧会提供拷贝构造函数
3)如果我们自己提供了拷贝构造函数,编译器就不会提供其他构造函数。
class Data
{
public:
int num;
public:
//构造函数(无参的构造)
Data()
{
num = 0;
cout<<"无参的构造函数 num = "<
3、拷贝构造函数(系统提供一个拷贝构造函数 赋值操作)
//拷贝构造函数
Data(const Data &ob)//const Data &ob = ob1
{
//拷贝构造函数 是ob2调用 num就是ob2的num
//ob2.num = ob1.num
num = ob.num;
cout<<"拷贝构造"<
void test03()
{
Data ob1(10);
cout<<"ob1.num = "<
记住一句话:旧对象 初始化 新对象 才会调用拷贝构造函数。
Data ob1(10);
Data ob2(ob1);//拷贝构造
Data ob3 = Data(ob1);//拷贝构造
Data ob4 = ob1;//拷贝构造函数
注意:下方的就不会调用拷贝构造
Data ob1(10);
Data ob2;
ob2 = ob1;//不会调用拷贝构造 单纯对象 赋值操作
4、拷贝构造函数的注意事项:
1)不能调用拷贝构造函数去初始化匿名对象,也就是说以下代码不正确
Data ob1(10);
Data(ob1); //ERROR Data(ob1) ==> Data ob1;造成ob1重复定义
2)对象作为函数的参数 如果实参与形参 都是普通对象 那么就会调用拷贝构造
5、深拷贝与浅拷贝的问题以及解决
1)浅复制 -----只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
深复制----在计算机中开辟了一块新的内存地址用于存放复制的对象。
2)在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
如果有属性开辟到堆区,利用编译器提供的拷贝构造函数就会调用浅拷贝带来的析构重复释放堆区空间的问题。
利用深拷贝(即自己提供拷贝构造函数)解决浅拷贝的问题。
#include
#include
#include
using namespace std;
class Person
{
private:
char *m_name;
int m_num;
public:
Person()
{
m_name = NULL;
m_num = 0;
cout<<"无参构造"<lucy
{
cout<<"拷贝构造函数"<



