C++_知识点——拷贝构造函数必须声明为引用

1.拷贝构造函数必须声明为引用

原因:避免拷贝构造函数无限制的递归而导致栈溢出。

#include <iostream>
using namespace std;

class A
{
    
    
private:
    int val;

public:
    A(int tmp) : val(tmp) // 带参数构造函数
    {
    
    
        cout << "A(int tmp)" << endl;
    }

    A(const A &tmp) // 拷贝构造函数
    {
    
    
        cout << "A(const A &tmp)" << endl;
        val = tmp.val;
    }

    A &operator=(const A &tmp) // 赋值运算符重载
    {
    
    
        cout << "A &operator=(const A &tmp)" << endl;
        val = tmp.val;
        return *this;
    }

    void fun(A tmp)
    {
    
    
    }
};

int main()
{
    
    
    A ex1(1);
    A ex2(2);
    A ex3 = ex1;
    ex2 = ex1;
    ex2.fun(ex1);
    return 0;
}
/*
运行结果:
A(int tmp)
A(int tmp)
A(const A &tmp)
A &operator=(const A &tmp)
A(const A &tmp)
*/

说明 1:ex2 = ex1; 和 A ex3 = ex1; 为什么调用的函数不一样?
对象 ex2 已经实例化了,不需要构造,此时只是将 ex1 赋值给 ex2,只会调用赋值运算符的重载;但是 ex3 还没有实例化,因此调用的是拷贝构造函数,构造出 ex3,而不是赋值函数,这里涉及到构造函数的隐式调用。

说明 2:如果拷贝构造函数中形参不是引用类型,A ex3 = ex1; 会出现什么问题?
构造 ex3,实质上是 ex3.A(ex1);,假如拷贝构造函数参数不是引用类型,那么将使得 ex3.A(ex1); 相当于 ex1 作为函数 A(const A tmp) 的实参,在参数传递时相当于 A tmp = ex1,因为 tmp 没有被初始化,所以在 A tmp = ex1 中继续调用拷贝构造函数,接下来的是构造 tmp,也就是 tmp.A(ex1) ,必然又会有 ex1 作为函数 A(const A tmp); 的实参,在参数传递时相当于即 A tmp = ex1,那么又会触发拷贝构造函数,就这下永远的递归下去

说明 3:为什么 ex2.fun(ex1); 会调用拷贝构造函数?
ex1 作为参数传递给 fun 函数, 即 A tmp = ex1;,这个过程会调用拷贝构造函数进行初始化。

2.什么情况下会调用拷贝构造函数:

直接初始化和拷贝初始化时

string dots("zhang"); //直接初始化
string dots = "zhang" //拷贝初始化

将一个对象作为实参传递给一个非引用或非指针类型的形参时
从一个返回类型为非引用或非指针的函数返回一个对象时
用花括号列表初始化一个数组的元素或者一个聚合类(很少使用)中的成员时

3.何时调用复制构造函数:

新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用。这在很多情况下都可能发生,最常见的情况是新对象显式地初始化为现有的对象。例如,假设 motto 是一个 StringBad 对象,则下面 4 种声明都将调用复制构造函数:

StringBad ditto(motto);
StringBad metoo = motto;
StringBad also = StringBad(motto);
StringBad * pStringBad = new StringBad(motto);

其中中间的 2 种声明可能会使用复制构造函数直接创建 metoo 和 also ,也可能使用复制构造函数生成一个临时对象,然后将临时对象的内容赋给 metoo 和 also,这取决于具体的实现。最后一种声明使用 motto 初始化一个匿名对象,并将新对象的地址赋给 pStringBad 指针。

猜你喜欢

转载自blog.csdn.net/weixin_43945471/article/details/134507214