对象作为参数和返回值 复制(拷贝)构造函数

时间:2022-09-14 23:26:28

先看一道搜狗的校园招聘题:
不考虑任何编译器优化(如:NRVO),下述代码的第10行会发生

#include <stdio.h>//1
class B//2
{//3
};//4
B func(const B& rhs){//5
  return rhs;//6
}//7
int main(int argc,char **argv){//8
  B b1,b2;//9
  b2=func(b1);//10
}//11

答案是 :一次拷贝构造函数,一次析构函数,一次赋值运算符。
为了说明这个问题,我们把代码改写一下,如下:

#include<iostream>
#include<stdio.h>

using namespace std;

class B
{
public:
    B()
    {
        cout<<"构造函数"<<endl;
    }
    ~B()
    {
        cout<<"析构函数"<<endl;
    }

    B(const B& b)
    {
        cout<<"拷贝构造函数"<<endl;
    }
    B& operator=(const B& b)
    {
        cout<<"赋值构造函数"<<endl;
    }
};

B func1(B& b)
{
    return b;
}

B func2(B b)
{
    return b;
}

B& func3(B b)
{
    return b;
}

int main()
{
    B b1,b2;
    cout<<endl;
    cout<<"以下是func1的结果:"<<endl;
    b2 = func1(b1);

    cout<<endl;
    cout<<"以下是func2的结果:"<<endl;
    b2 = func2(b1);
    cout<<endl;

    cout<<endl;
    cout<<"以下是func3的结果:"<<endl;
    b2 = func3(b1);
    cout<<endl;

    return 0;
}

结果如下:
对象作为参数和返回值 复制(拷贝)构造函数
主要考察的是赋值构造函数的用法,使用赋值构造函数有3中情况:
1 明确表示由一个对象初始化另一个对象时:
B b1; B b2(b1);

2 当对象作为函数实参传递给函数形参时,会调用拷贝构造函数,生成一个无名的局部对象,但如果参数是引用或者是指针,则不用调用拷贝构造函数:
func2(b1)

3 当一个自动存储类对象作为函数返回值时,会调用拷贝构造函数生成一个无名的对象,返回给函数,,但如果返回值类型是引用或者指针,则 不调用:
return b;

对于func1,
参数是引用类型,不调用构造函数,不生成临时对象
在返回对象时,首先要用拷贝构造函数生成一个无名对象并返回给调用函数,所以有一次拷贝构造函数,
接着执行赋值操作,调用赋值运算符,
此时,func1运行结束,无名的对象被释放,调用一次析构函数。

对于func2,
传递参数时调用一次拷贝构造函数,生成一个无名对象,
返回时,调用一次拷贝构造函数,又生成一个无名对象,
接着是赋值运算符,调用赋值运算符
func2结束,释放无名对象,因为有两个,所以调用两次析构函数。

对于func3:
传递参数时调用一次拷贝构造函数,生成一个无名对象,
返回时,因为是引用,所以不调用,也不生成对象
接着是赋值运算符,调用赋值运算符
func3结束,释放无名对象,因为只有一个,所以调用一次析构函数

注意:在func3中,有些编译器可能编译出错。因为无名变量是局部变量,在函数运行结束时就会释放,引用这样的变量会导致程序出现错误。