先說點背景知識,調用復制構造函數的三種情況:
1.當用類一個對象去初始化另一個對象時。
2.如果函數形參是類對象。
3.如果函數返回值是類對象,函數執行完成返回調用時。
在輔導學生上機時,有同學第3點提出異議。有教材上的例題為證:
~~~
#include <iostream>
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;
}
~~~
證據是,在CodeBlocks中,運行結果是:

而不是期望的:

顯然,第3種情況下,復制構造函數沒有被執行。
確定問題后,我知道道理是對的,看過的幾本書,厚的、薄的,都是這么寫的。會不會是編譯器的差別?CodeBlocks用的是gcc。gcc開源,跟標準的變化跟得緊,莫不是第3種情況已經成了老黃歷,而各種書來不及變?
我讓她到VC++6.0中運行。一會兒她的反饋,在VC++6.0中復制構造函數執行了。
真相明白了。
這個問題需要有個交待。
回家后再翻各種書,無果。網絡搜索,CSDN上有個貼子[《函數返回值是對象,是調用了拷貝構造函數?》](http://bbs.csdn.net/topics/390803716),其中大家給的結論,是gcc做了優化,返回值為對象時,不再產生臨時對象,因而不再調用復制構造函數。
看來不是標準發生變化。
那如果一定想要讓這個構造函數執行呢?只需讓忽略gcc不要搞這個優化就行了。貼子中給了個線索,在新浪博客《[命名返回值優化](http://blog.sina.com.cn/s/blog_4ab8464c0100kybj.html)》。文章稱通過搜索知道“這是一個稱為命名返回值優化的問題,而且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”選項起了作用,有圖為證。下面中加上這個參數后,編譯完看到的提示信息:
