C 基礎教程物件導向(學習筆記(29))

2021-08-29 13:58:24 字數 4259 閱讀 1303

重新初始化型別

由於我們將在接下來的幾節課中談論很多初始化,讓我們首先回顧一下c ++支援的初始化型別:直接初始化,統一初始化或拷貝初始化。

以下是使用我們的fraction類的所有示例:

#include #include class fraction

friend std::ostream& operator<<(std::ostream& out, const fraction &f1);};

std::ostream& operator<<(std::ostream& out, const fraction &f1)

我們可以直接初始化:

int x(5); // 用乙個整數直接初始化

fraction fivethirds(5, 3); // 直接初始化乙個fraction, 呼叫fraction(int, int) 建構函式

在c ++ 11中,我們可以進行統一初始化:

int x ; //用乙個整數統一初始化

fraction fivethirds ; // 統一初始化乙個fraction, 呼叫fraction(int, int) 建構函式

最後,我們可以進行拷貝初始化:

int x = 6; // 用乙個整數複製初始化

fraction six = fraction(6); // 複製初始化乙個fraction,將呼叫fraction(6,1)

fraction seven = 7; //複製初始化fraction。編譯器將嘗試找到將7轉換為fraction的方法,該方法將呼叫fraction(7,1)建構函式。

通過直接和統一的初始化,直接初始化正在建立的物件。但是,複製初始化稍微複雜一些。我們將在下一課中更詳細地**複製初始化。但為了有效地做到這一點,我們需要先放下這一點。

拷貝建構函式

現在考慮以下程式:

#include #include class fraction

friend std::ostream& operator<<(std::ostream& out, const fraction &f1);};

std::ostream& operator<<(std::ostream& out, const fraction &f1)

int main()

如果你編譯這個程式,你會看到它編譯得很好,並產生結果:

5/3讓我們仔細看看這個程式是如何工作的。

變數fivethirds的初始化只是乙個標準的直接初始化,它呼叫fraction(int,int)建構函式。非常正常。但下一行怎麼樣?變數fcopy的初始化顯然也是直接初始化,你知道建構函式用於初始化類。那麼這行呼叫的建構函式是什麼?

答案是這行是呼叫fraction的拷貝建構函式。乙個拷貝建構函式是用來建立乙個新的物件,為現有物件的拷貝建構函式的一種特殊型別。與預設構造函 數非常相似,如果您沒有為類提供拷貝建構函式,c ++將為您建立乙個公共複製建構函式。由於編譯器對您的類知之甚少,因此預設情況下,建立的拷貝建構函式使用稱為成員初始化的初始化方法。 成員初始化只是意味著副本的每個成員都直接從被拷貝的類的成員初始化。在上面的例子中,fcopy.m_numerator將從fivethirds.m_numerator等初始化…

就像我們可以顯式定義預設建構函式一樣,我們也可以顯式定義拷貝建構函式。拷貝建構函式看起來就像你期望的那樣:

#include #include class fraction

// 拷貝建構函式

fraction(const fraction &fraction) :

m_numerator(fraction.m_numerator), m_denominator(fraction.m_denominator)

// 注意:我們可以直接訪問引數fraction的成員,因為我們在fraction類中

friend std::ostream& operator<<(std::ostream& out, const fraction &f1);};

std::ostream& operator<<(std::ostream& out, const fraction &f1)

int main()

執行此程式時,您將獲得:

copy constructor called

5/3我們在上面的例子中定義的拷貝建構函式使用成員初始化,並且在功能上等同於我們預設獲得的拷貝建構函式,除了我們新增了乙個輸出語句來說明正在呼叫拷貝建構函式。

與預設建構函式(您應該始終提供自己的預設建構函式)不同,如果滿足您的需要,可以使用預設的拷貝建構函式。

乙個有趣的注意事項:您已經看到了一些過載operator 《的例子,我們可以訪問引數f1的私有成員,因為該函式是fraction類的友元函式。類似地,類的成員函式可以訪問相同類型別的引數的私有成員。由於我們的fraction複製建構函式接受類型別的引數(以複製),我們能夠直接訪問引數fraction的成員,即使它不是隱式物件。

防止複製

我們可以通過將拷貝建構函式設為私有來防止產生的類的副本:

#include #include class fraction

public:

// 預設建構函式

fraction(int numerator=0, int denominator=1) :

m_numerator(numerator), m_denominator(denominator)

friend std::ostream& operator<<(std::ostream& out, const fraction &f1);};

std::ostream& operator<<(std::ostream& out, const fraction &f1)

int main()

現在,當我們嘗試編譯程式時,我們將得到編譯錯誤,因為fcopy需要使用拷貝建構函式,但由於拷貝建構函式已宣告為私有,因此無法看到它。

可以省略拷貝建構函式

現在考慮以下示例:

#include #include class fraction

// 拷貝建構函式

fraction(const fraction &fraction) :

m_numerator(fraction.m_numerator), m_denominator(fraction.m_denominator)

friend std::ostream& operator<<(std::ostream& out, const fraction &f1);};

std::ostream& operator<<(std::ostream& out, const fraction &f1)

int main()

考慮一下該程式的工作原理 首先,我們使用fraction(int,int)建構函式直接初始化乙個匿名fraction物件。然後我們使用該匿名fraction物件作為fraction fivethirds的初始化器。由於匿名物件是乙個fraction,這應該呼叫拷貝建構函式,對嗎?

執行它並自己編譯。你可能希望得到這個結果(你可以):

copy constructor called

5/3但實際上,你更有可能(但不能保證)得到這個結果:

5/3為什麼我們的複製建構函式沒有被呼叫?

請注意,初始化匿名物件然後使用該物件來指導初始化我們定義的物件需要兩個步驟(乙個用於建立匿名物件,乙個用於呼叫拷貝建構函式)。但是,最終結果基本上與僅進行直接初始化相同,只需要一步。

因此,在這種情況下,允許編譯器選擇不呼叫拷貝建構函式,而只是進行直接初始化。這個過程叫做elision。

所以儘管你寫道:

fraction fivethirds(fraction(5, 3));
編譯器可能會將其更改為:

fraction fivethirds(5, 3);
它只需要乙個構造函式呼叫(到fraction(int,int))。請注意,在使用elision的情況下,拷貝建構函式體中的任何語句都不會執行,即使它們會產生其他作用作用(如列印到螢幕上)!

最後,請注意,如果您將拷貝建構函式設為private,那麼使用拷貝建構函式的任何初始化都將導致編譯錯誤,即使拷貝建構函式被省略了!

C 基礎教程物件導向(學習筆記5(2))

在編寫具有多個建構函式的類 大多數建構函式 時,必須為每個建構函式中的所有成員指定預設值會導致冗餘 如果更新成員的預設值,則需要觸控每個建構函式。從c 11開始,可以直接為普通類成員變數 不使用static關鍵字的變數 提供預設初始化值 class rectangle void print int ...

C 基礎教程物件導向(學習筆記(23))

過載一元運算子 與您目前看到的運算子不同,正 負 和邏輯非 運算子都是一元運算子,這意味著它們只能在乙個運算元上執行。因為它們僅對它們所應用的物件進行操作,所以通常將一元運算子過載實現為成員函式。所有三個運算元都以相同的方式實現。讓我們看一下我們如何在前面的例子中使用的cents類上實現operat...

C 基礎教程物件導向(學習筆記(24))

過載比較運算子相對簡單,因為它們遵循我們在過載其他運算子時看到的相同模式。因為比較運算子都是不修改左運算元的二元運算子,所以我們將使過載的比較運算子宣告為友元函式。這是乙個帶有過載運算子 和operator!的car類的示例。include include class car friend bool...