先說點背景知識,調用復制構造函數的三種情況:
1.當用類一個對象去初始化另一個對象時。
2.如果函數形參是類對象。
3.如果函數返回值是類對象,函數執行完成返回調用時。
在輔導學生上機時,有同學第3點提出異議。有教材上的例題為證:
#include證據是,在CodeBlocks中,運行結果是:using namespace std; class Point //Point 類的定義 { public: Point(int xx=0, int yy=0) { x = xx; //構造函數,內聯 y = yy; } Point(const Point& p); //復制構造函數 void setX(int xx) { x=xx; } void setY(int yy) { y=yy; } int getX() const { return x; //常函數(第5章) } int getY() const { return y; //常函數(第5章) } private: int x, y; //私有數據 }; //成員函數的實現 Point::Point (const Point& p) { x = p.x; y = p.y; cout << "Calling the copy constructor " << endl; } //形參為Point類對象的函數 void fun1(Point p) { cout << p.getX() << endl; } //返回值為Point類對象的函數 Point fun2() { Point a(1, 2); return a; } //主程序 int main() { Point a(4, 5); //第一個對象A Point b = a; //情況一,用A初始化B。第一次調用復制構造函數 cout << b.getX() << endl; fun1(b); //情況二,對象B作為fun1的實參。第二次調用復制構造函數 b = fun2(); //情況三,函數的返回值是類對象,函數返回時調用復制構造函數 cout << b.getX() << endl; return 0; }
而不是期望的:
顯然,第3種情況下,復制構造函數沒有被執行。
確定問題後,我知道道理是對的,看過的幾本書,厚的、薄的,都是這麼寫的。會不會是編譯器的差別?CodeBlocks用的是gcc。gcc開源,跟標准的變化跟得緊,莫不是第3種情況已經成了老黃歷,而各種書來不及變?
我讓她到VC++6.0中運行。一會兒她的反饋,在VC++6.0中復制構造函數執行了。
真相明白了。
這個問題需要有個交待。
回家後再翻各種書,無果。網絡搜索,CSDN上有個貼子《函數返回值是對象,是調用了拷貝構造函數?》,其中大家給的結論,是gcc做了優化,返回值為對象時,不再產生臨時對象,因而不再調用復制構造函數。
看來不是標准發生變化。
那如果一定想要讓這個構造函數執行呢?只需讓忽略gcc不要搞這個優化就行了。貼子中給了個線索,在新浪博客《命名返回值優化》。文章稱通過搜索知道“這是一個稱為命名返回值優化的問題,而且g++的這個編譯優化竟然沒有直接的關閉方法給出解決辦法”。作者是用命令行工作的,他後來解決的辦法,是在編譯命令中加上“-fno-elide-constructors”參數,例g++ -fno-elide-constructors testReturn.cpp。
我的學生還處在用IDE的階段。本文的價值來了,如何在CodeBlocks下也忽略這個優化項呢?
在CodeBlocks中,通過菜單依次選:settings->Compiler...,在Global compiler settings部分,選擇Other options,在文本框中寫入“-fno-elide-constructors”,如圖,然後就可以ok啦。
然後,如同苦難的公主終於和王子過上了幸福的生活一樣,期望的結果有了。
“-fno-elide-constructors”選項起了作用,有圖為證。下面中加上這個參數後,編譯完看到的提示信息: