1、考虑下面的代码,请问输出结果是什么?
#include <stdio.h>
class A {
public:
A(int val) {
puts("A(int)");
d = val;
}
A(A&& a) {
puts("A(A&&)");
d = a.d;
}
A(A& a) {
puts("A(A&)");
d = a.d;
}
~A() {}
private:
int d;
};
A create_A(int val) {
return A(val);
}
int main(void) {
A a{create_A(5)};
return 0;
}
如果你认为应该输出下面的结果,那就不用继续往下看了。
$ g++ ./main.cpp
$ ./a.out
A(int)
2、对于上面的输出结果,是不是或多或少有点出乎意料。从输出结果来看,只调用了一次构造函数,而且 移动构造函数 和 拷贝构造函数 都没有被调用。为什么会这样?是因为编译器对返回值做了优化,何以见得?我们可以打印一下create_A()中临时对象的地址,然后再打印一下main函数中对象a的地址,会发现他们是一样的。
#include <stdio.h>
class A {
public:
A(int val) {
puts("A(int)");
d = val;
}
A(A&& a) {
puts("A(A&&)");
d = a.d;
}
A(A& a) {
puts("A(A&)");
d = a.d;
}
~A() {}
private:
int d;
};
A create_A(int val) {
A a(val);
printf("create_A()::&a = %p\n", &a);
return a;
}
int main(void) {
A a{create_A(5)};
printf("main()::&a = %p\n", &a);
return 0;
}
程序运行结果如下:
$ g++ ./main.cpp
$ ./a.out
A(int)
create_A()::&a = 0x7ffee7bd08f8
main()::&a = 0x7ffee7bd08f8
3、如果开启了-fno-elide-constructors编译选项,结果就更容易理解了
$ g++ -fno-elide-constructors main.cpp
$ ./a.out
A(int)
create_A()::&a = 0x7ffee73018b8
A(A&&)
A(A&&)
main()::&a = 0x7ffee73018f8
从输出结果来看,拷贝构造函数被调用了两次,一次是create_A()内部创建的对象返回后构造一个临时对象产生的,另一次是在main函数中构造对象a产生的,同时,create_A()中对象a的地址与main函数中对象a的地址不相同。