关于浅拷贝和深拷贝中需要注意的点
-拷贝函数的调用时机
// 1.直接以创建好的对象初始化
Person p1(20);
Person p2(p1);
// 2.以值传递的方式
Person p;
doWork(p);
// 3.以值方式返回局部变量
Person p1;
return p1;// 局部变量,并不是返回上一行的p1,而是拷贝一个返回
-情况:当成员中需要在堆上申请空间时。当构造对象时把对象当成参数传入的时候,在没有自己编写深拷贝构造函数,编译器会自动调用浅拷贝函数。浅拷贝就是直接把传入参数的成员一一对应的赋值给构造对象,当成员不是堆上的数据时,这样子没有问题。但是当某个成员属性是在堆上的数据,那么编译器只会把传入对象的成员的指向复制给构造对象的成员。这样子传入对象和构造对象就有某个成员属性指向堆上的同一块内存。总所皆知类中如果有堆上申请的成员,那么在析构函数中则必须自己去释放。这个时候由于传入对象和构造对象都有属性指向堆上同一个内存。那么当程序执行完后,执行析构函数时,会按照先进后出的顺序执行析构函数,就会出现重复释放的问题。
- 解决:自己编写深拷贝函数,防止出现浅拷贝。
//自己的的通用的数组类
#pragma once
#include<iostream>
using namespace std;
template<class T>
class MyArry {
private:
//指向堆区开辟的数组
T* pAddress;
public:
MyArry(int capacity) {
cout << "有参构造函数调用" << endl;
this->pAddress = new T[this->mCapacity];
}
~MyArry() {
if (this->pAddress) {
cout << "析构函数的调用" << endl;
delete[] this->pAddress;
this->pAddress = NULL;
}
}
//拷贝构造
MyArry(const MyArry& arr) {
this->pAddress = new T[arr.mCapacity];
//复制数据过来
for (int i = 0; i < arr.mSize; i++) {
this->pAddress[i] = arr.pAddress[i];
}
}
//operator= 防止浅拷贝
// 使用=构造的前提必须是=左边的对象是已经存在的对象
//为什么返回的是一个引用而不是指针
/*
1.返回局部对象的指针是一个很危险的操作。当函数返回时候
,局部对象会被销毁。那么就会得到一个悬空指针。
2.返回引用是一贯用法。
3.防止浅拷贝,并且允许链式赋值a=b=c,因为c++中=是返回引用
*/
MyArry& operator=(const MyArry& arr) {
//先判断原来堆区是否有数据
//有自己原来有数据,先释放,再拷贝
if (this->pAddress) {
delete[] this->pAddress;
this->pAddress = NULL;
}
this->pAddress = new T[arr.mCapacity];
//复制数据过来
for (int i = 0; i < arr.mSize; i++) {
this->pAddress[i] = arr.pAddress[i];
}
//这里返回*this,表示是返回this的指针,而不是对象的地址
//允许链式赋值
//返回指针this是错误的,因为函数的返回值要求返回一个引用,而引用和指针是两种类型
return *this;
}
};