学习完类中的默认成员函数 -- 构造函数和析构函数
下一个学习的默认成员函数是拷贝构造函数--那么拷贝构造函数与前两个函数又有哪些区别呢???????
1.拷贝构造函数首先我们先知道它与构造函数构成重载(故函数名相同)
我们先写一个最简单拷贝函数
class Data
{
public:
//构造函数
Data(int year = 2021, int month = 5, int day = 5)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Data(Data d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Data D1;
Data D2(D1);
return 0;
}
简单分析上面代码 : 创建D1对象 , 将D1对象拷贝给D2(D2调用拷贝构造函数,将参数D1的内容拷贝构造传给d ,在将d的内容的成员变量赋值给D2,最终完成拷贝。)
2.拷贝构造函数的参数只有一个且必须使用引用传参,使用传值的方式会引发无穷的递归调用
上面分析看似非常合理,通过表面分析没有任何破绽 -- 其实不然,不妨运行看看
发现报错了 --报的问题是非法的复制构造函数
那么这个到底是什么原因导致的呢?????????
仔细想想刚刚我们的分析 —————— D1的内容拷贝给d(这里调用了拷贝构造函数),
最关键的来了 -- 调用的拷贝函数还要接着将D1的内容拷贝给d(再调用拷贝构造函数)
一步步的拷贝构造函数递归下去(死循环)
如下图:
那么我们是知道了,只需要解决在传参时的拷贝问题(也就能解决这个死循环的问题)。
我们的引用就登场了,直接让d为D1的别名,这样子就解决了传参时的拷贝问题!
我们来通过代码实践--实践出真知
class Data
{
public:
//构造函数
Data(int year = 2021, int month = 5, int day = 5)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Data(Data& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Data D1;
Data D2(D1);
return 0;
}
运行结果如下图 --
D1的内容拷贝给了D2,那么我们的拷贝构造函数接近完成
还差最后一步 看如下代码
//拷贝构造函数
Data(Data& d)
{
d._year=_year;
_month = d._month;
_day = d._day;
}
我们将_year=d._year写成d._year=_year 这样子不久将D2拷贝的内容出错,连D1都出现了问题
将D1的_year成员变量都变成了随机值
那么我们就应该保证D1的内容不可改 ,在形参前加const进行修饰
直接给我们报错提醒
若未显示定义拷贝构造函数,系统会默认生成拷贝构造函数
1.默认生成拷贝构造函数 : 内置类型成员 , 会完成按字节序的拷贝(浅拷贝)
2.自定义类型成员会调用它的拷贝构造
我们还是拿上面的代码验证 -- 默认拷贝构造函数做了哪些事
class Data
{
public:
//构造函数
Data(int year = 2021, int month = 5, int day = 5)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Data D1;
//拷贝复制 -- 默认生成的拷贝构造函数
Data D2(D1);
return 0;
}
发现默认生成的拷贝构造函数和我们自定义的拷贝构造函数,也能一样的将D1拷贝给D2
(字节序拷贝)
拷贝构造函数是浅拷贝 --- 浅拷贝又会带来什么问题呢
class Stack
{
private:
int* _a;
size_t _top;
size_t _capacity;
public:
Stack(int capacity = 4)
{
_a = (int*)malloc(sizeof(int) * capacity);
if (_a == nullptr)
{
cout << "malloc fail" << endl;
exit(-1);
}
_capacity = capacity;
_top = 0;
}
//析构函数
~Stack()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
};
int main()
{
//自动调用构造函数
Stack s1(5);
Stack s2(s1);
return 0;
}
运行上面程序--发现崩溃了 (我们通过调试来看看)
发现对象S2对象内malloc开辟的空间与S1对象内_a 指向的是同一块空间
我们在析构时对同一块空间析构两次 (程序崩溃了)
最后总结 : 拷贝构造我们不写生成的默认拷贝构造函数对于内置类型和自定义类型都会拷贝处理,但是处理的细节是不一样的,这个跟构造和析构是不一样的