用為實值型別的 C++ 類別

C++ 類別預設為實值型別。 它們可以指定為參考型別,讓多型行為支援面向物件程序設計。 實值型別有時會從記憶體和版面配置控件的觀點檢視,而參考型別則與基類和虛擬函式有關,以便進行多型用途。 根據預設,實值類型是可複製的,這表示一律會有複製建構函式和複製指派運算符。 針對參考型別,您可以將類別設為不可複製的類別(停用複製建構函式和複製指派運算符),並使用支援其預期多型的虛擬解構函式。 實值型別也與內容有關,這些內容在複製時,一律會提供兩個可個別修改的獨立值。 參考型別與身分識別有關 - 其對象類型為何? 因此,「參考型別」也稱為「多型型別」。

如果您真的想要類似參考的類型(基類、虛擬函式),您需要明確地停用複製,如下列程式代碼中的 類別所示 MyRefType

// cl /EHsc /nologo /W4

class MyRefType {
private:
    MyRefType & operator=(const MyRefType &);
    MyRefType(const MyRefType &);
public:
    MyRefType () {}
};

int main()
{
    MyRefType Data1, Data2;
    // ...
    Data1 = Data2;
}

編譯上述程式代碼會導致下列錯誤:

test.cpp(15) : error C2248: 'MyRefType::operator =' : cannot access private member declared in class 'MyRefType'
        meow.cpp(5) : see declaration of 'MyRefType::operator ='
        meow.cpp(3) : see declaration of 'MyRefType'

實值類型和移動效率

因為新的複製優化,所以避免複製配置額外負荷。 例如,當您在字串向量中間插入字串時,不會有複製重新配置的額外負荷,只有移動,即使它會導致向量本身的成長。 這些優化也適用於其他作業:例如,在兩個巨大的物件上執行新增作業。 如何啟用這些值作業優化? 編譯程式會隱含地為您啟用它們,就像編譯程式可以自動產生複製建構函式一樣。 不過,您的類別必須透過在類別定義中宣告指派和移動建構函式來「選擇加入」。 Move 會在適當的成員函式宣告中使用雙和 (&&) 右值參考,並定義移動建構函式和移動指派方法。 您也需要插入正確的程序代碼,以從來源物件中「竊取腸道」。

如何決定是否需要啟用移動作業? 如果您已經知道需要啟用複製建構,您可能也想要啟用移動建構,特別是如果它比深層複製便宜。 不過,如果您知道您需要移動支援,不一定表示您想要啟用複製作業。 後者會稱為「僅限移動類型」。 標準連結庫中已經有一個範例是 unique_ptr。 另請注意,舊 auto_ptr 版本已被取代,而且正是因為舊版 C++ 中缺少移動語意支援而取代 unique_ptr

藉由使用行動語意,您可以依值傳回或中間插入。 移動是複製的優化。 不需要堆積配置作為因應措施。 請考慮下列虛擬程式碼:

#include <set>
#include <vector>
#include <string>
using namespace std;

//...
set<widget> LoadHugeData() {
    set<widget> ret;
    // ... load data from disk and populate ret
    return ret;
}
//...
widgets = LoadHugeData();   // efficient, no deep copy

vector<string> v = IfIHadAMillionStrings();
v.insert( begin(v)+v.size()/2, "scott" );   // efficient, no deep copy-shuffle
v.insert( begin(v)+v.size()/2, "Andrei" );  // (just 1M ptr/len assignments)
//...
HugeMatrix operator+(const HugeMatrix& , const HugeMatrix& );
HugeMatrix operator+(const HugeMatrix& ,       HugeMatrix&&);
HugeMatrix operator+(      HugeMatrix&&, const HugeMatrix& );
HugeMatrix operator+(      HugeMatrix&&,       HugeMatrix&&);
//...
hm5 = hm1+hm2+hm3+hm4+hm5;   // efficient, no extra copies

啟用適當實值類型的移動

對於移動比深層複本便宜的類似值類別,請啟用移動建構和移動指派以提高效率。 請考慮下列虛擬程式碼:

#include <memory>
#include <stdexcept>
using namespace std;
// ...
class my_class {
    unique_ptr<BigHugeData> data;
public:
    my_class( my_class&& other )   // move construction
        : data( move( other.data ) ) { }
    my_class& operator=( my_class&& other )   // move assignment
    { data = move( other.data ); return *this; }
    // ...
    void method() {   // check (if appropriate)
        if( !data )
            throw std::runtime_error("RUNTIME ERROR: Insufficient resources!");
    }
};

如果您啟用複製建構/指派,如果移動建構/指派比深層複本便宜,也可以啟用移動建構/指派。

某些 非實值 型別是僅移動類型,例如當您無法複製資源時,只能轉移擁有權。 範例:unique_ptr

另請參閱

C++ 類型系統
歡迎回到 C++
C++ 語言參考
C++ 標準程式庫