浅拷贝:两个对象所指向的地址是同一个地址
深拷贝:两个对象的值相同,并且他们都有独立的地址
对于C++而言,如果不提供自己写的拷贝构造函数,编译器会生成默认的
1.将目标对象的值逐个拷贝过来
2.如果传递的是指针的话,所拷贝的是指针的值,也就是指向的地址,而不是指向的对象(浅拷贝)
3.在析构函数中释放内存时,其他对象中的指针可能还在指向已经被释放的地址,如果再次调用析构函数,则会delete被释放的资源,导致报错
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <iostream> #include <string> #include <vector> using namespace std; class Person { private: string name{"name"}; double *height {nullptr}; public: string get_name() {return name;}; double get_height(){return *height}; Person(string name = "none",double height = 0.0); ~Person(); }
Person::Person(string name,double height) { this->name = name; this->height = new double {height}; }
Person::~Person(){ if (height!=nullptr) delete height; } int main() { Person Mike {'Mike',175.0}; Person new_person {Mike}; return 0; }
|
在上述例子中,在Person类中定义的指向Person的height属性的指针,并没有定义自身的拷贝构造函数,因此编译器会默认产生浅拷贝的拷贝构造函数,与以下函数等同:
1 2 3 4 5 6
| Person(const Person &source) { this->name = source.name this->height = source.height }
|
默认拷贝构造函数是以值传递的形式进行的,因此它会把Mike的name属性赋值给new_person的name,把指向Mike的height属性的地址也会传给new_person的地址,因此new_person和Mike两个对象的height属性的地址指向的是同一个地址。而在程序结束是将会执行析构函数释放每个变量的内存,因为在栈上面定义,因此遵循后人先出的规则,先释放new_person的内存,将占有heigt属性的地址释放后继续释放Mike的height属性的地址,因为这两个指针的地址一样,因此,第二次释放时,会导致报错。
解决这个问题的方式可以通过自定义拷贝构造函数:
1 2 3 4 5 6 7 8 9
| Person(const Person &source);
Person::Person(const Person &source) { this->name = source.name; this->height = new double {*(source.height)}; }
|
如此就会创建一个新的变量,并将传入的变量的指针解引用后的值传递给函数中,并在堆中申请一块新的地址指向新的变量,因此两个变量都拥有独立的值和地址,这样执行析构函数就不会报错了。
__END__