为什么拷贝构造函数参数必须为引用?

今天朋友问我一个特别有意思的点,eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Person{
private:
int m_age;
int *m_height;
public:
//构造函数
Person(int a, int h) {
m_age = p.m_age;
//堆区
m_height = new int(h);
}
//拷贝构造函数
Person(const Person& p) {
cout << "拷贝构造函数!" << endl;
//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
m_age = p.m_age;
//m_height = p.m_heigh (浅拷贝,系统默认拷贝构造函数)  //m_height 是指针
m_height = new int(*p.m_height);
}

};

int main(){
Person p1(1,2);
Person p2(p1);
}

无论是系统默认还是自定义的拷贝构造函数,都是传一个引用,而不是,这是为什么呢?

naive的我想了想:节省一次值拷贝(赋值给形参),节省空间?

当然不会那么简单。若拷贝构造函数参数为值传递,当实参赋值给形参时,又发生了一次拷贝函数的调用,无限递归下去,导致爆栈。

1
2
3
4
5
6
//拷贝构造函数变成这样  
Person(const Person p);

Person p2(p1);
// 等价于p2(Person temp = p1),此时又调用一次拷贝构造函数(赋值引起),变成 p2(Person temp(p1))
// p2(Person temp(p1)) 又等价于p2(Person temp(Person temp2 = p1),赋值引起拷贝,变成 p2(Person temp( Person temp2(p1)))...

这个例子还要注意一点:m_height开辟在堆区,利用赋值运算符 operator=进行类对象赋值时,因此不能只是做简单浅拷贝(防止析构时野指针的出现—-一个内存地址有两个指向的指针),因此需要自定义拷贝构造函数。

结论:拷贝构造函数参数为引用,不为值传递是为了防止拷贝构造函数的无限递归,最终导致栈溢出。

各种构造函数也是重载,编译器自动找寻合适的函数。

PS:没事多看看C++ Primer,讲的细又深刻。 名字查找优于类型查找,其本质是作用域

---------------- 本文结束 ----------------

本文标题:为什么拷贝构造函数参数必须为引用?

文章作者:Pabebe

发布时间:2021年04月09日 - 21:08:03

最后更新:2021年04月16日 - 19:58:56

原始链接:https://pabebezz.github.io/article/c13c7aad/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%