引言
在 C++ 面向对象编程中,对象的复制操作无处不在。无论是函数传参、返回值传递,还是对象间的赋值,都需要精确控制数据的复制行为。C++ 通过拷贝构造函数和赋值运算符重载两套机制,为开发者提供了对象复制的完整解决方案。
一、拷贝构造函数
如果一个构造函数的第一个参数是自身类型的引用,且其他所有参数都有默认值(如果有),就叫做拷贝构造,它是特殊的构造函数。
1.1 解析:拷贝构造特点
拷贝构造函数遵循一些基本规则,理解这些规则有助于避免常见的陷阱:
- 拷贝构造函数是构造函数的一个重载。
- 第一个参数必须是自身类类型的引用:
类名&或const 类名&(建议加const)。如果使用传值的方式,逻辑上会引发无穷递归调用,因为形参本身就需要通过拷贝构造来初始化。 - 可以有多个参数,但第一个必须为引用,其余必须有缺省值。
- 自定义类型对象进行拷贝行为必须调用拷贝构造。因此,自定义类型传值传参和传值返回都会触发拷贝构造。
- 编译器生成的默认拷贝构造:若未显式定义,编译器会生成。它对内置类型成员变量完成值拷贝(浅拷贝),对自定义类型成员变量则调用其拷贝构造。
- 浅拷贝与深拷贝的选择:对于像
Date类这样成员全是内置类型且不指向资源的类,默认拷贝构造足够。但对于像Stack类这样有指针指向资源的类,默认浅拷贝会导致析构时重复释放内存,需要显式定义深拷贝。 - 传值返回与传引用返回:传值返回会产生临时对象调用拷贝构造;传引用返回不产生拷贝,但需注意返回对象的生命周期,避免返回局部对象的引用导致野指针。
关于传参方式的补充: 虽然指针可以传参,但如果构造函数参数是指针,它会被视为普通构造函数而非拷贝构造。
浅拷贝的后果演示: 当两个对象共享同一块动态分配的资源时,修改其中一个会影响另一个。更严重的是,析构时会对同一块空间释放两次,导致程序崩溃。
typedef int STDataType;
class Stack {
public:
Stack(int n = 4) {
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a) {
perror("malloc 申请空间失败");
return;
}
_capacity = n;
_top = 0;
}
// 浅拷贝示例
Stack(const Stack& s) {
_a = s._a;
_capacity = s._capacity;
_top = s._top;
}
void Push {
(_top == _capacity) {
newcapacity = _capacity * ;
STDataType* tmp = (STDataType*)(_a, newcapacity * (STDataType));
(tmp == ) {
();
;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
~() {
cout << << endl;
(_a);
_a = ;
_top = _capacity = ;
}
:
STDataType* _a;
_capacity;
_top;
};


