[英]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 ++因這種無償復制而受到批評,這種無聲復制是很難避免的,並且可能導致性能降低。 這是幽默提到的,例如在這里 :
您不小心創建了一打自己的實例,然后用腳射擊。 提供緊急醫療援助是不可能的,因為您無法分辨出哪些是按位復制的,哪些是指向他人的,並說:“那是我,那邊。”
當聲明任何函數/方法按值接收參數時,就會發生這種復制。 它是構造函數,“獨立”函數還是方法都沒有關系。 為了避免這種情況,請使用const
引用:
CopyAbleComposer(const CopyAble& m1) : memObj(m1)
{
...
}
注意:即使您按照以下方式重新排列代碼,也始終保留一份副本。 長期以來,這一直是C ++的主要缺陷。
CopyAbleComposer cac(CopyAble(10)); // initializing mem1 by a temporary object
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.