簡體   English   中英

C ++復制構造函數對成員初始化的雙重調用

[英]C++ copy constructor double call on member initialization

考慮下面的代碼,其中正在實例化一個以另一個類作為其成員的組成類:

class CopyAble {
private:
    int mem1;
public:
    CopyAble(int n1) : mem1(n1) {
        cout << "Inside the CopyAble constructor" << endl;
    }
    CopyAble(const CopyAble& obj) {
        cout << "Inside the CopyAble copy constructor" << endl;
        this->mem1 = obj.mem1;
        return *this;     
    }

    CopyAble& operator=(const CopyAble& obj) {
        cout << "Inside the  CopyAble assignment constructor" << endl;
        this->mem1 = obj.mem1;
    }
    ~CopyAble() {};
};

class CopyAbleComposer {
    private:
        CopyAble memObj;
    public:
        CopyAbleComposer(CopyAble m1) : memObj(m1) {
            cout << "Composing the composer" << endl;
        }
        ~CopyAbleComposer() {}
};

int main()
{
    CopyAble ca(10);
    CopyAbleComposer cac(ca);
    return 0;
}

運行此命令時,將得到輸出:

Inside the CopyAble constructor  
Inside the CopyAble copy constructor  
Inside the CopyAble copy constructor  
Composing the composer  

這意味着該CopyAble復制構造函數要運行兩次 -一次是將CopyAble對象傳遞到CopyAbleComposer構造函數中,另一次是運行初始化程序memObj(m1)時。

這是復制構造函數的慣用用法嗎? 當我們嘗試使用相同類型的傳入對象初始化成員對象時,復制構造函數運行兩次是非常低效的,而且似乎很多C ++程序員很容易陷入陷阱而沒有意識到這一點。

編輯:我不認為這是關於將引用傳遞給副本構造函數的問題的重復。 在這里,我們被迫將引用傳遞給常規構造函數以避免重復對象的創建,我的問題是這是否眾所周知,C ++中的類構造函數應通過引用傳遞對象以避免這種重復副本?

您應該在CopyAbleComposer(CopyAble m1)處通過引用接受CopyAble ,否則將調用復制構造函數來構造參數。 您還應該將其標記為explicit以避免意外調用:

explicit CopyAbleComposer(const CopyAble & m1)

值傳遞和關聯的復制是C ++的一個眾所周知的屬性。 實際上,在過去,C ++因這種無償復制而受到批評,這種無聲復制是很難避免的,並且可能導致性能降低。 這是幽默提到的,例如在這里

您不小心創建了一打自己的實例,然后用腳射擊。 提供緊急醫療援助是不可能的,因為您無法分辨出哪些是按位復制的,哪些是指向他人的,並說:“那是我,那邊。”

C ++ 98

當聲明任何函數/方法按值接收參數時,就會發生這種復制。 它是構造函數,“獨立”函數還是方法都沒有關系。 為了避免這種情況,請使用const引用:

CopyAbleComposer(const CopyAble& m1) : memObj(m1)
{
    ...
}

注意:即使您按照以下方式重新排列代碼,也始終保留一份副本。 長期以來,這一直是C ++的主要缺陷。

CopyAbleComposer cac(CopyAble(10)); // initializing mem1 by a temporary object

C ++ 11

C ++ 11引入了移動語義 ,該語義通過“移動”操作代替了附加副本,該操作比復制更為有效:在通常情況下,對象動態分配內存時,“移動”僅重新分配一些指針,而“復制”分配和取消分配內存。

要從移動語義提供的優化中受益,您應該撤消對C ++ 98所做的“優化”,並按值傳遞參數。 另外,在初始化mem1成員時,應調用move構造函數:

    CopyAbleComposer(CopyAble m1) : memObj(std::move(m1)) {
        cout << "Composing the composer" << endl;
    }

最后,您應該實現move構造函數:

CopyAble(CopyAble&& obj) {
    cout << "Inside the CopyAble move constructor" << endl;
    this->mem1 = obj.mem1;
}

然后,您應該看到“復制”消息沒有出現,而是由“移動”消息代替。

有關更多詳細信息,請參見此問題


注意:在所有這些示例中,假定CopyAble對象要復雜得多,並且copy和move構造函數執行的任務很簡單(通常是資源管理)。 在現代C ++中,在關注點分離的背景下,資源管理被視為單獨的關注 也就是說,任何需要非默認副本或移動構造函數的類都應盡可能小。 這也稱為零規則

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM