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的地址不相同。