C++拷贝构造函数和运算符重载--1 https://developer.aliyun.com/article/1424584
请注意上面说的,默认拷贝构造只会进行浅拷贝,当我们在对象成员中开辟了动态空间时,使用默认拷贝构造将会出现问题。请看以下代码:
//此代码运行时将会崩溃 #include <iostream> using namespace std; typedef int DataType; class Stack { public: Stack(size_t capacity = 10) { _array = (DataType*)malloc(capacity * sizeof(DataType)); if (!_array) { perror("malloc申请空间失败"); return; } _size = 0; _capacity = capacity; } ~Stack() { if (_array) { free(_array); _array = nullptr; _capacity = 0; _size = 0; } } private: DataType* _array; size_t _size; size_t _capacity; }; int main() { Stack s1; Stack s2(s1); return 0; }
分析:
首先,s1先调用构造函数创建,在构造函数中开辟了10个元素的空间,然后,s2对象使用s1拷贝构造,Stack对象中没有自己定义,系统将生成一份默认的拷贝构造函数进行浅拷贝,这时,s1和s2栈结构将同时指向一块内存空间,当函数退出时,s2和s1会自动调用析构函数进行销毁,这就会造成s1和s2指向的同一块空间销毁两次,系统崩毁。
解决上面的问题不难,我们只需将s1和s2分别指向不同的空间即可,这时,需要我们自己定义拷贝构造函数。
#include <iostream> using namespace std; typedef int DataType; class Stack { public: Stack(size_t capacity = 10) { _array = (DataType*)malloc(capacity * sizeof(DataType)); if (!_array) { perror("malloc申请空间失败"); return; } _size = 0; _capacity = capacity; } //定义拷贝构造 Stack(const Stack& S) { _size = S._size; _capacity = S._capacity; _array = (DataType*)malloc(_capacity * sizeof(DataType)); if (!_array) { perror("malloc申请空间失败"); return; } } ~Stack() { if (_array) { free(_array); _array = nullptr; _capacity = 0; _size = 0; } } private: DataType* _array; size_t _size; size_t _capacity; }; int main() { Stack s1; Stack s2(s1); return 0; }
总:如果类中没有涉及空间资源的申请时,拷贝构造函数可以不写,但是一旦涉及到资源空间的申请时,则拷贝构造函数是一定要写上的。
拷贝构造函数典型调用场景:
1,使用已存在对象创建新对象。如同以上Stack类中s2的创建。
/*..........*/ int main() { Stack s1; Stack s2(s1);//使用已存在的对象s1来创建对象s2 return 0; }
2,函数参数类型为类类型对象——解析:因为在传参过程中,形参就相当于实参的临时拷贝,相当于用实参来创建形参。
3,函数返回值类型为类类型对象——解析:当函数返回时,函数内局部对象的生命周期结束,但是临时变量的值会被拷贝到调用函数的栈帧中,或者通过引用传递给调用函数。当返回类类型对象时,直接将此类对象拷贝到调用函数栈帧中。
总结一句话,只要是运用了类对象与类对象直接赋值进行初始化的情况,系统就会调用拷贝构造。而为了提高效率,一般能用引用就用引用。
C++拷贝构造函数和运算符重载--3 https://developer.aliyun.com/article/1424591?spm=a2c6h.13148508.setting.30.214f4f0emw3QR7