先看一道搜狗的校园招聘题:
不考虑任何编译器优化(如: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中,有些编译器可能编译出错。因为无名变量是局部变量,在函数运行结束时就会释放,引用这样的变量会导致程序出现错误。