描述
Problem test01()
{
Problem p1;
cout << "p1的地址:" << &p1 << endl;
return p1;
}
void test02()
{
Problem p2 = test01(); //这里相当于隐式转换
cout << "p2的地址:" << &p2 << endl;
}
结果
从构造函数的角度出发,显然存在问题,这里只用了一次构造函数,而且p1和p2的地址是一样的。
这是由于编译器的优化导致形成的,主要是返回值优化和复制省略造成的。
具体
在非优化的情况下:
在test01的返回值不是p1本身,而是通过拷贝构造函数生成的一个新的值,可以理解为生成了一个临时匿名类对象p’,p’的再传递给新的类对象p2,这个传递相当于一个隐式转换法(这里的转换也使用了一次拷贝函数),如下例子因此p1和p2的地址不会相等。此过程中应当使用两次构造函数,两次次拷贝函数。
Person p4 = 10; // Person p4 = Person(10);
cout << &p4 << endl;
Person p5 = p4; // Person p5 = Person(p4);
cout << &p5 << endl;
/* p5这里使用的是拷贝构造,这里发生拷贝,但是地址不会被拷贝,所以这里地址不同 */
编译器优化的情况下:
在C++11及以后中采用了一种新的编译优化技术(复制省略和返回值优化),因为函数返回类对象会通过拷贝构造函数生产临时类对象,这样会导致效率低下,在编译器优化后,会省略拷贝环节。
解决
不建议直接返回类对象,尽量不使用该方法去传递。
若真要解决,可以关闭编译器的优化。
在使用GNU/g++ 编译器时可以使用"-fno-elide-constructors"选项来强制g++总是调用copy构造函数,即使在用临时对象初始化另一个同类型对象的时候。
官方解释:
-fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary that is only used to initialize another object of the same type. Specifying this option disables that optimization, and forces G++ to call the copy constructor in all cases. This option also causes G++ to call trivial member functions which otherwise would be expanded inline.
In C++17, the compiler is required to omit these temporaries, but this option still affects trivial member functions.
using namespace std;
完整代码:
class Problem
{
public:
Problem()
{
cout << "构造函数的调用" << endl;
}
Problem(int a)
{
age = a;
cout << "有参构造函数的调用" << endl;
}
Problem(const Problem &p)
{
age = p.age;
cout << "拷贝构造函数的调用" << endl;
}
~Problem()
{
cout << "析构函数的调用" << endl;
}
int age;
};
Problem test01()
{
Problem p1;
cout << "p1的地址:" << &p1 << endl;
return p1;
}
void test02()
{
Problem p3();
Problem p2 = test01();
cout << "p2的地址:" << &p3 << endl;
}
/*
* @Description: main
* @param - l: 无
* @return: 无
*/
int main()
{
system("chcp 65001 > nul");
Problem p3;
test02();
// system("pause");
return 0;
}