一、什么是拷贝构造函数?
拷贝构造函数是一种特殊的构造函数,它的参数是同类型对象的常量引用,即用已存在的一个变量初始化另一个同类型变量。
classPerson{public:// 拷贝构造函数Person(constPerson&other){name=other.name;age=other.age;cout<<"拷贝构造被调用"<<endl;}// 其他...private:string name;intage;};二、拷贝构造函数的语法规则
函数名与类名相同。
没有返回值。
参数必须是同类型对象的引用,通常为 const 。
可以自定义,也可以让编译器自动生成。
classMyClass{public:// 拷贝构造函数的典型形式MyClass(constMyClass&other);};为什么参数要写引用
- 如果类没有管理动态资源(比如没有 new 分配内存),我们其实只需要把原对象的值复制给新对象就够了。那能不能直接把原对象的值当作普通参数传给拷贝构造函数,让它用这个值来初始化新对象呢?
- 答案是不行,原因在于:
- C++规定:传值传参必须调用拷贝构造。而我们若在实现拷贝构造时使用传值传参,则又会生成新的拷贝构造,如此不断生成新的拷贝构造,最后会造成死循环。
三、何时调用拷贝构造函数?
用一个对象初始化另一个对象
Personp1("Alice",25);Person p2=p1;// 调用拷贝构造Personp3(p1);// 调用拷贝构造函数参数按值传递
voidfunc(Person p){...}func(p1);// 实参p1拷贝给形参p,调用拷贝构造函数按值返回对象
Personcreate(){Persontemp("Bob",30);returntemp;// 返回值时可能调用拷贝构造(或移动语义)}容器中的元素插入
当容器需要复制元素时,会调用拷贝构造。(如 vector 的 push_back)
四、默认拷贝构造函数
编译器自动生成的拷贝构造
- 如果类中没有定义拷贝构造函数,编译器会自动生成一个,它执行浅拷贝(shallow copy):逐个复制成员变量的值。
什么时候需要自己写拷贝构造?
- 当类中有指针成员并指向动态分配的内存时,浅拷贝会导致两个对象共享同一块内存(应该为新对象申请一块新的内存,需要自己手动实现,若执行浅拷贝,则会直接),释放时会产生重复释放或内存泄漏。
classShallow{public:int*data;Shallow(intval){data=newint(val);}// 编译器生成的默认拷贝构造:data = other.data(浅拷贝)};intmain(){Shallowa(10);Shallow b=a;// 两个对象的data指向同一块内存!// 析构时会重复释放,导致未定义行为。}五、深拷贝:自定义拷贝构造函数
- 为了正确复制包含动态资源的对象,我们需要自定义拷贝构造函数,执行深拷贝(deep copy):申请一块新的空间,再复制原指针指向的内容。
classDeep{int*data;public:Deep(intval){data=newint(val);}// 深拷贝构造Deep(constDeep&other){data=newint(*other.data);// 重新分配并复制值}~Deep(){deletedata;}};intmain(){Deepa(10);Deep b=a;// 深拷贝,各自拥有独立内存// 安全销毁}六、拷贝构造与赋值运算符的区别
- 拷贝构造:用已有对象初始化新对象(对象尚未创建)。
- 赋值运算符:将已有对象的值赋给已存在的对象(对象已创建)。
Personp1("Alice",25);Person p2=p1;// 拷贝构造(p2尚未创建)Personp3("Bob",30);p3=p1;// 赋值运算符(p3已存在)七、禁止拷贝
- 如果类不希望被复制,可以显式删除拷贝构造函数(C++11):
classNonCopyable{public:NonCopyable()=default;NonCopyable(constNonCopyable&)=delete;// 禁止拷贝};八、如何使用
如果有资源的申请分配,一般要自己实现。
如果自定义了析构函数、拷贝构造、拷贝赋值其中之一,通常需要自定义其他每个。
优先使用深拷贝:当类管理动态内存时,务必实现深拷贝。
用 const 引用:避免不必要的拷贝,提高性能。
使用移动语义(C++11)减少拷贝开销,适合临时对象。
九、完整示例
#include<iostream>#include<cstring>usingnamespacestd;classString{char*buf;public:String(constchar*s=""){buf=newchar[strlen(s)+1];strcpy(buf,s);}// 拷贝构造(深拷贝)String(constString&other){buf=newchar[strlen(other.buf)+1];strcpy(buf,other.buf);cout<<"拷贝构造"<<endl;}~String(){delete[]buf;}voidshow()const{cout<<buf<<endl;}};intmain(){Strings1("Hello");String s2=s1;// 调用拷贝构造s1.show();s2.show();return0;}十、总结
拷贝构造函数控制对象如何被复制,是类设计的重要一环。
浅拷贝适合简单类,管理资源的类需要深拷贝。
编译器默认生成拷贝构造,但自定义更灵活、更安全。