行业知识
【C++修行之道】类和对象(三)拷贝构造函数
2026-11-01 11:56:57
来源:互联网转载
目录
一、 概念
二、特征
正确的拷贝构造函数写法:
拷贝函数的另一种写法
三、若未显式定义,编译器会生成默认的拷贝构造函数。
四、编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?
深拷贝的写法:
五、拷贝构造函数典型调用场景:
六、总结:
一、 概念
在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
二、特征
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
- 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
- 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?
- 拷贝构造函数典型调用场景:
- 使用已存在对象创建新对象
- 函数参数类型为类类型对象
- 函数返回值类型为类类型对象
class Date{public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;} Date(const Date& d) // 正确写法 { // this->_year = d._year; _year = d._year; _month = d._month; _day = d._day; }Date(Date& d)// 错误写法: 它不能用于从常量对象或临时对象进行拷贝构造{_year = d._year;_month = d._month;_day = d._day;}Date(Date d)// 错误写法:编译报错,会引发无穷递归{_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;};int main(){Date d1; // 拷贝构造:用同类型的对象拷贝初始化Date d2(d1);return 0;}正确的拷贝构造函数写法:
Date(const Date& d) // 正确写法{// this->_year = d._year;_year = d._year;_month = d._month;_day = d._day;}这是正确的拷贝构造函数写法。它接受一个对Date类型的常量引用作为参数,这意味着它可以用于从常量对象、非常量对象甚至是临时对象进行拷贝构造。由于它的灵活性,这是最常用的拷贝构造函数定义方式。
Date(Date& d){_year = d._year;_month = d._month;_day = d._day;}- 不能接收常量对象:这个构造函数只接受非常量引用(Date&),这意味着你不能使用它来拷贝一个常量对象。如果试图这样做,编译器会报错,因为常量对象不能被非常量引用所绑定。
- 不能接收临时对象:在C++中,临时对象(也称为右值)经常出现在表达式中,例如函数返回值或者类型转换的结果。由于这个拷贝构造函数不接受右值引用或常量引用,因此它不能用于拷贝这些临时对象。
- 发生错误操作时没有报错:赋值反了
Date(Date d)// 错误写法:编译报错,会引发无穷递归{_year = d._year;_month = d._month;_day = d._day;}这个构造函数是错误的,会引发无穷递归。原因在于,当试图用这个构造函数创建一个Date对象时,它会尝试以值传递的方式接收一个Date对象作为参数。为了构造这个参数对象d,又需要调用拷贝构造函数,这会导致无限递归调用,最终耗尽栈空间并导致程序崩溃。
class Date{public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}/*Date(Date& d){d._year = _year;d._month = _month;d._day = _day;}*/// Date d3(d2);//Date(const Date& d)//{//// this->_year = d._year;//_year = d._year;//_month = d._month;//_day = d._day;//}Date(Date* d){_year = d->_year;_month = d->_month;_day = d->_day;}void Print(){cout