关于c++的拷贝构造函数.
书中写到:
在C++中,下面三种对象需要调用拷贝构造函数!
对象以值传递的方式传入函数参数
对象以值传递的方式从函数返回
对象需要通过另外一个对象进行初始化;
针对上面的第二条,按照书中例子写出如下代码:
#include <iostream>
using namespace std;
class Point
{
public:
Point(int xx = 0, int yy = 0) :x(xx),y(yy){}
Point(Point &p){
x = p.x;
y = p.y;
cout << "copy function is Called" << endl;
}
int GetX(){ return x; }
int GetY(){ return y; }
private:
int x, y;
};
Point fun()
{
cout << "fun() Called\n";
Point t(2, 2);
return t;
}
int main()
{
Point t1(1,1);
Point t2 = fun();
cout << t2.GetX()<< endl;;
}
以上代码能在visual studio 2013 环境 下顺利编译通过
输出为
fun() Called
copy function is Called
x is 2
当在g++环境下编译时,却出现了错误
C:\Users\Hale\Desktop\test1>g++ -std=c++11 test22.cpp -o test22
test22.cpp: In function 'int main()':
test22.cpp:30:17: error: no matching function for call to 'Point::Point(Point)'
Point t2 = fun();
^
test22.cpp:30:17: note: candidates are:
test22.cpp:8:2: note: Point::Point(Point&)
Point(Point &p){
^
test22.cpp:8:2: note: no known conversion for argument 1 from 'Point' to 'Point&'
test22.cpp:7:2: note: Point::Point(int, int)
Point(int xx = 0, int yy = 0) :x(xx),y(yy){}
^
test22.cpp:7:2: note: no known conversion for argument 1 from 'Point' to 'int'
C:\Users\Hale\Desktop\test1>g++ -std=c++11 test22.cpp -o test22
test22.cpp:8:15: error: invalid constructor; you probably meant 'Point (const Point&)'
Point(Point p){
^
没有匹配的方法?于是添加构造函数
Point(const Point &p){
x = p.x;
y = p.y;
cout << "const copy function is Called" << endl;
}
再次编译,成功!
结果如下
fun() Called
2
但是刚刚新增的构造方法的方法并没有被调用,但是结果是正确的。说好的作为函数返回对象会调用构造函数呢?
g++为什么会有这种处理?
书上未能找到相关答案,网上搜寻也没能获得满意答案!
怪我咯2017-04-17 15:36:42
當在g++環境下編譯時,卻出現了錯誤:沒有匹配的方法?
報錯原因是你的拷貝建構子參數型別寫的不對,少了const
修飾符。在C++中,臨時變數是不能綁定到非常量引用的。而樓主程式中函數Point fun()
的回傳值是臨時變量,拷貝構造函數Point(Point &p)
的參數是非常量引用,所以編譯器會報錯,提示沒有匹配的函數。當把拷貝建構函數的參數改成常數引用後,臨時變數就可以正常綁定,所以可以編譯通過了。例如:
// 给出如下函数声明
int f();
void g1(int &i);
void g2(const int &i);
int n = 1;
// 则调用结果为
g1(0); // compile error
g1(n); // ok
g1(f()); // compile error
g2(0); // ok
g2(n); // ok
g2(f()); // ok
剛剛新增的構造方法的方法並沒有被調用,但是結果是正確的。說好的作為函數返回物件會呼叫建構函數呢?
這是因為C++標準中明確規定了編譯器可以在一定情況下對函數傳回值的拷貝/移動操作進行最佳化。
ISO C++11標準§8.5/16中作瞭如下規定(省略了上下文):
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have eff opects .... 🎜>... In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being inized;
在§12.8/31有:
所以,這只是編譯器最佳化而已。樓主可以試試用Visual Studio的Release模式進行編譯,應該會跟g++一樣跳過拷貝建構子的。
回覆0
高洛峰2017-04-17 15:36:42
查閱相關資料發現,在c++標準中有【回傳值最佳化】。
gcc/g++之所以沒有呼叫拷貝建構函數,是因為gcc/g++將相關的操作做了最佳化,將沒有必要的拷貝操作給省略掉了。具體解釋可以參考知乎某答住的解釋:
GCC是這樣乾的。凡是需要回傳一個結構體的函數,呼叫這個函數的地方,會額外傳給這個函數一個位址。之後函數就將回傳值直接建構到這個位址那裡。這樣的好處在於,返回值直接就構造在了適當的地方,不需要進行額外的拷貝構造。算是一個非常巧妙的實現方式,避免了不必要的拷貝構造。給GCC點個讚! !
作者:李其邁 連結:https://www.zhihu.com/questio...來源:知乎著作權歸作者所有。商業轉載請聯絡作者授權,非商業轉載請註明出處。
將visual studio的Debug模式改為Release發現也將這些沒必要的操作給優化了
結果
fun() Called
x is 2
所以問題得以解決。