先看看下面这段有问题的代码:
#include<iostream>
using namespace std;
class A
{
public:
A(int a) :x(a)
{ cout << "Constructor Called, and x = " << x << endl; }
A(A& a) :x(a.x)
{
cout << "Copy Constructor Called, and x = " << x <<endl;
}
void print() { cout << "print function called, and x = " << x << endl; }
A ReturnA() { A a(1); return a; }
A& ReturnRef() { A a(3); return a; }
~A() { cout << "Deconstructor Called, and x = " << x << endl; }
private:
int x;
};
int main()
{
A ref = A(2);//此处会报错(1)
ref.print();
cout << endl;
A refB = ref.ReturnA();//此处也会报错(2),此处仅有一次析构。
refB.print();
cout << endl;
ref.ReturnA();//没有对象接收,两次析构
cout << endl;
A& refC = ref.ReturnRef();
refC.print();
}
using namespace std;
class A
{
public:
A(int a) :x(a)
{ cout << "Constructor Called, and x = " << x << endl; }
A(A& a) :x(a.x)
{
cout << "Copy Constructor Called, and x = " << x <<endl;
}
void print() { cout << "print function called, and x = " << x << endl; }
A ReturnA() { A a(1); return a; }
A& ReturnRef() { A a(3); return a; }
~A() { cout << "Deconstructor Called, and x = " << x << endl; }
private:
int x;
};
int main()
{
A ref = A(2);//此处会报错(1)
ref.print();
cout << endl;
A refB = ref.ReturnA();//此处也会报错(2),此处仅有一次析构。
refB.print();
cout << endl;
ref.ReturnA();//没有对象接收,两次析构
cout << endl;
A& refC = ref.ReturnRef();
refC.print();
}
(1)对于(1)处的错误试图用一个A类型的匿名对象初始化一个A的对象,在新的编译器进行优化处理后,该操作只会调用普通的构造函数,但是A类的拷贝构造函数参数类型为A&(即A的非从const引用),对于匿名对象,以值返回的对象,编译器会把这两个都当做是临时对象处理,对于临时对象是不允许被修改的,编译器为了阻止这种行为,会禁止将临时对象赋值给非const的引用,所以会报错,原因就是拷贝构造函数参数类型为非const的引用。
(2)其实也是同理,用返回的临时对象去初始化一个A的对象,会调用构造和拷贝构造,但是临时对象和非const的引用(A&)参数列表是不匹配的(编译器禁止了这种行为)。
所以处理办法就是修改拷贝构造函数的传入参数类型为const 引用,即:
A(const A& a) :x(a.x)
{
cout << "Copy Constructor Called, and x = " << x <<endl;
}
{
cout << "Copy Constructor Called, and x = " << x <<endl;
}
这样程序就正确了,运行结果如图:
正如所言,编译器进行了优化,匿名对象将不会调用拷贝构造(早期编译会),只会调用普通构造函数,将匿名对象直接变为我们构造的对象。
另外针对该程序输出可知,当以值返回局部对象时会调用拷贝构造函数,如果有对象来接收该返回值时就会用局部对象拷贝构造并赋值或者初始化它,但是如果没有对像来接收它,则仍然会调用拷贝构造函数生成临时对象但是马上销毁
这是有对象接收时(用于初始化)(一次析构:局部对象)
这是没有对象接收时(两次析构:局部对象和临时对象)
对于所讲的示例,在举一个简单的例子:
总之,常量,临时变量(包括:匿名对象,以值返回的对象)(会被编译器视作常量,即右值),是不允许赋值给非const的引用(常常会导致函数调用时实参与形参类型不符),只能赋值给const的引用。