瞭解臨時對象的來源
c++真正的所謂的臨時對象是不可見的——不會再你的原始碼中出現。此等匿名對象通常發生於兩種情況:一是當隱式類型轉換(implicit type conveersions)被施行起來以求函數調用能夠成功;二是當函數返回對象的時候。
第一種情況的例子:
代碼如下 |
複製代碼 |
#include <iostream>
class Int { public: Int(int value) { _value = value; std::cout << "In constructor, count: " << ++count << std::endl; } ~Int() { std::cout << "In destructorn"; }
int _value; private: static int count; };
int Int::count = 0;
void printInt(Int intValue) { std::cout << "int value is: " << intValue._value << std::endl; }
int main() { printInt(10); return 0; } |
這個例子的運行結果是:
In constructor, count: 1
int value is: 10
In destructor
驗證了編譯器再發現沒法調用printInt(int)的時候,會進行自動類型轉換,通過Int的建構函式,將int隱式轉換成Int(這個貌似之前也提到過了)。這個構造出來的就是所謂的臨時對象,從輸出的順序看,這個對象在函數返回之後被析構。
只有當對象以by value(傳值)方式傳遞,或是當對象被傳遞給一個reference-to -const參數時,這些轉換才會發生。如果參數被傳遞給一個reference-to-non-const參數,並不會發生此類轉換
也就是說,把上面的printInt入參改成Int&的時候,就無法調用成功。原因很簡單了,這個臨時對象,如果沒修改,函數返回之後也被析構了,沒法再次擷取到,即使允許也是沒意義的。改成Int&之後,編譯器(g++)有這樣的報錯:
tmp.cpp: 在函數‘int main()’中:
tmp.cpp:27:13: 錯誤:用類型為‘int’的右值初始化類型為‘Int&’的非常量引用無效
tmp.cpp:21:6: 錯誤:在傳遞‘void printInt(Int&)’的第 1 個實參時
利用重載(overload)避免隱式類型轉換(implicit type conversions)
這裡介紹的就是通過重載,避免編譯器自動通過建構函式建立臨時對象,但是重載太多後面的維護什麼的成本也會上升,需要考慮這個的必要性。
每個“重載操作符”必須獲得至少一個“使用者定製類型”的自變數
協助完成傳回值最佳化(RVO)
另一個容易產生臨時對象的地方就是函數傳回值。不過經過嘗試,發現目前編譯器(g++)可以把命名變數也通過傳回值最佳化去除。
也就是說,這樣寫:
代碼如下 |
複製代碼 |
#include <iostream>
class Int { public: Int(int value) { _value = value; std::cout << "In constructor, count: " << ++count << " value: " << _value <<std::endl; } Int(const Int &rhs) { _value = rhs._value; std::cout << "In copy constructor, count: " << ++count << " value: " << _value << std::endl; }
friend const Int operator+(const Int &lhs, const Int &rhs);
private: int _value; static int count; };
int Int::count = 0; const Int operator+(const Int &lhs, const Int &rhs) { Int result(lhs._value + rhs._value); return result; }
int main() { Int a(1); Int b(2); Int c = a + b;
return 0; } |
和這樣寫:
代碼如下 |
複製代碼 |
#include <iostream>
class Int { public: Int(int value) { _value = value; std::cout << "In constructor, count: " << ++count << " value: " << _value <<std::endl; } Int(const Int &rhs) { _value = rhs._value; std::cout << "In copy constructor, count: " << ++count << " value: " << _value << std::endl; }
friend const Int operator+(const Int &lhs, const Int &rhs);
private: int _value; static int count; };
int Int::count = 0; const Int operator+(const Int &lhs, const Int &rhs) { return Int(lhs._value + rhs._value); }
int main() { Int a(1); Int b(2); Int c = a + b;
return 0; } |
最終的執行結果都是一樣的:
In constructor, count: 1 value: 1
In constructor, count: 2 value: 2
In constructor, count: 3 value: 3
沒有因為前者在operator+中多了一個result對象而多一個臨時變數。