概念
我们在创建对象时,可否创建一个与一个对象一模一样的新对象呢?
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特征
拷贝构造函数也是特殊的成员函数,其特征如下:
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
无穷调用具体来说:
当一个对象以值方式传递时,编译器会生成代码调用它的拷贝构造函数生成一个复本。如果类A的拷贝构造函数是以值方式传递一个类A对象作为参数的话,当需要调用类A的拷贝构造函数时,是以值方式传进一个A的对象作为实参; 而以值方式传递需要调用类A的拷贝构造函数;结果就是调用类A的拷贝构造函数导致又一次调用类A的拷贝构造函数,这就是一个无限递归。
拷贝构造函数能不能用指针作为参数?
代码不会报错,可以正确输出。但是这并不是一个拷贝构造函数了,拷贝构造函数的参数必须是引用类型的才可以。所以说用指针作为参数,这其实是一个构造函数,参数类型为A*
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date(const 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;
}
以传值方式传参数:
- 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做 浅拷贝,或者值拷贝。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1;
// 这里d2调用的默认拷贝构造完成拷贝,d2和d1的值也是一样的。
Date d2(d1);
return 0;
}
实现一个String类(深、浅拷贝)
要点总结:
- 拷贝构造函数指的是参数为本类其他对象的引用的构造函数,他在给对象初始化成本类其他对象时调用。系统会自动提供一个拷贝构造函数。
知识点习题
- 下列情况中,不会调用拷贝构造函数的是()。
A.用一个对象去初始化同一类的另一个新对象时
B.将类的一个对象赋值给该类的另一个对象时
C.函数的形参是类的对象,调用函数进行形参和实参结合时
D.函数的返回值是类的对象,函数执行返回调用时
正确答案:
B
答案解析:
拷贝构造函数调用的时机:
- 当用类的一个对象初始化该类的另一个对象时
- 如果函数的形参是类的对象,调用函数时,进行形参和实参结合时.
- 如果函数的返回值是类的对象,函数执行完成返回调用者时.
- 需要产生一个临时类对象时(B选项调用的是赋值运算符重载)
- 以下代码共调用多少次拷贝构造函数:
Widget f(Widget u)
{
Widget v(u);
Widget w=v;
return w;
}
main(){
Widget x;
Widget y=f(f(x));
}
A. 1
B. 3
C. 5
D. 7
正确答案
D
答案解析
在实例化类对象a时调用的是构造函数,然后将
- 实参与形参结合时会调用一次拷贝构造函数;
- 初始化局部变量时调用一次拷贝构造函数;
- 给局部变量赋值时调用一次(初始化的时候赋值调用的是拷贝构造函数);
- 函数返回并与形参进行结合时再调用一次。所以第一次调用func()函数时调用了4次。
- 在此调用时不需要再将实参与形参结合(第一个f 的返回值直接作为第二个f 的参数,这是编译器优化)
- 直接初始化调用一次,赋值调用一次,函数返回赋值给调用一次。所以第二次调用func()函数时调用了3次。
如果有不同见解,欢迎留言讨论!!!