C++基礎教程面向物件(學習筆記(30))
阿新 • • 發佈:2018-12-16
複製初始化
考慮以下程式碼行:
int x = 5;
此語句使用拷貝初始化將新建立的整數變數x初始化為值5。
但是,類更復雜一些,因為它們使用建構函式進行初始化。本課將介紹與類的拷貝初始化相關的主題。
拷貝類的初始化
鑑於我們的Fraction類:
#include <cassert> #include <iostream> class Fraction { private: int m_numerator; int m_denominator; public: // 預設建構函式 Fraction(int numerator=0, int denominator=1) : m_numerator(numerator), m_denominator(denominator) { assert(denominator != 0); } friend std::ostream& operator<<(std::ostream& out, const Fraction &f1); }; std::ostream& operator<<(std::ostream& out, const Fraction &f1) { out << f1.m_numerator << "/" << f1.m_denominator; return out; }
考慮以下:
int main()
{
Fraction six = Fraction(6);
std::cout << six;
return 0;
}
如果你要編譯並執行它,你會發現它產生了預期的輸出: 6/1 這種形式的複製初始化的評估方式與以下相同:
Fraction six(Fraction(6));
正如您在上一課中所瞭解到的,這可能會呼叫Fraction(int,int)和Fraction拷貝建構函式(由於效能原因可能會被省略)。但是,由於不能保證刪除,最好避免類的拷貝初始化,而是使用直接或統一初始化。
規則:避免使用複製初始化,而是使用統一初始化。
其他地方使用拷貝初始化
還有一些其他地方使用了拷貝初始化,但其中兩個值得明確提及。按值傳遞或返回類時,該程序使用複製初始化。
考慮:
#include <cassert> #include <iostream> class Fraction { private: int m_numerator; int m_denominator; public: //預設建構函式 Fraction(int numerator=0, int denominator=1) : m_numerator(numerator), m_denominator(denominator) { assert(denominator != 0); } // 拷貝建構函式 Fraction(const Fraction ©) : m_numerator(copy.m_numerator), m_denominator(copy.m_denominator) { // 這裡不需要檢查0的分母,因為副本必須已經是有效的分數 std::cout << "Copy constructor called\n"; // just to prove it works } friend std::ostream& operator<<(std::ostream& out, const Fraction &f1); int getNumerator() { return m_numerator; } void setNumerator(int numerator) { m_numerator = numerator; } }; std::ostream& operator<<(std::ostream& out, const Fraction &f1) { out << f1.m_numerator << "/" << f1.m_denominator; return out; } Fraction makeNegative(Fraction f) // 理想情況下我們應該通過const引用來做到這一點 { f.setNumerator(-f.getNumerator()); return f; } int main() { Fraction fiveThirds(5, 3); std::cout << makeNegative(fiveThirds); return 0; }
在上面的程式中,函式makeNegative按值獲取分數,並返回按值分數。當我們執行這個程式時,我們得到: Copy constructor called Copy constructor called -5/3 當fiveThirds作為引數傳遞給makeNegative()引數f時,會發生第一個拷貝建構函式呼叫。當makeNegative()的返回值傳遞迴main()時,會發生第二次呼叫。
在上面的例子中,不能省略通過值傳遞的引數和返回值。但是,在其他情況下,如果引數或返回值滿足特定條件,編譯器可能會選擇忽略拷貝建構函式。例如:
class Something
{
};
Something foo()
{
Something s;
return s;
}
int main()
{
Something s = foo();
}
在這種情況下,編譯器可能會忽略拷貝建構函式,即使變數s是按值返回的。