簡體   English   中英

在c ++中使用私有拷貝構造函數有什么用處

[英]What's the use of the private copy constructor in c++

為什么人們定義私有拷貝構造函數?

何時使復制構造函數和賦值運算符私有化是一個好的設計?

如果類中沒有成員是唯一對象(如文件名)的指針或句柄,那么在其他情況下,私有拷貝構造函數是個好主意嗎?

同樣的問題適用於賦值運算符。 鑒於大多數C ++圍繞復制對象並通過引用傳遞,是否有任何涉及私有拷貝構造函數的好設計?

一個用例是單例模式 ,其中只能有一個類的實例 在這種情況下,您需要使構造函數和賦值operator = private,以便無法創建多個對象。 創建對象的唯一方法是通過GetInstance()函數,如下所示。

// An example of singleton pattern
class CMySingleton
{
public:
  static CMySingleton& GetInstance()
  {
    static CMySingleton singleton;
    return singleton;
  }

// Other non-static member functions
private:
  CMySingleton() {}                                  // Private constructor
  ~CMySingleton() {}
  CMySingleton(const CMySingleton&);                 // Prevent copy-construction
  CMySingleton& operator=(const CMySingleton&);      // Prevent assignment
};

int main(int argc, char* argv[])
{
  // create a single instance of the class
  CMySingleton &object = CMySingleton::GetInstance();

  // compile fail due to private constructor
  CMySingleton object1;
  // compile fail due to private copy constructor
  CMySingleton object2(object);
  // compile fail due to private assignment operator
  object1 = object;

  // ..
  return 0;
}

某些對象表示不能或不應復制的特定實體。 例如,您可以阻止復制表示應用程序使用的日志文件的對象,這與期望代碼的所有部分將使用單個日志文件相對應。 使用意外或不正確復制的對象可能導致日志中出現無序內容,當前日志大小的不准確記錄,多次嘗試(某些失敗)“滾動”到新的日志文件名或重命名現有文件名。

另一個用途是通過虛函數強制執行復制。 由於構造函數不能是virtual ,因此通常的做法是阻止直接訪問復制構造函數並提供virtual Base* clone()方法,該方法返回指針指向的實際運行時類型的副本。 這可以防止Base b(derived)出現的意外切片。

另一個例子:一個死簡單的智能指針對象,它簡單地刪除它在構造函數中給出的指針:如果它不支持引用計數或其他一些處理多個所有者的方式,並且不希望有風險笨拙的意外std::auto_ptr樣式的所有權轉移,然后簡單地隱藏復制構造函數給出了一個很棒的小智能指針,它對於可用的有限情況快速有效。 關於嘗試復制它的編譯時錯誤會有效地要求程序員“嘿 - 如果你真的想這樣做會把我改成共享指針,否則就會退回!”。

一個非常糟糕的例子:

class Vehicle : { int wheels; Vehicle(int w) : wheels(w) {} }

class Car : public Vehicle { Engine * engine; public Car(Engine * e) : Vehicle(4), engine(e) }

...

Car c(new Engine());

Car c2(c); // Now both cars share the same engine!

Vehicle v;
v = c; // This doesn't even make any sense; all you have is a Vehicle with 4 wheels but no engine.

“復制”汽車意味着什么? (汽車是汽車模型,還是汽車的實例?復制它是否可以保留車輛登記?)

將車輛分配給另一輛車是什么意思?

如果操作沒有意義(或僅僅是未實現),那么標准的做法是使復制構造函數和賦值運算符保持私有,如果使用它們而不是奇怪的行為,則會導致編譯錯誤。

將復制構造函數和復制賦值設置為私有的常見原因是禁用這些操作的默認實現。 但是,在C ++ 0x中,為此目的有特殊的syntax = delete 因此,在C ++ 0x中,復制ctor private似乎被重新定義為非常奇特的情況。

復制ctors和作業是相當的語法糖; 所以這種“私人糖”似乎是貪婪的症狀:)

即使對象的內容不是指針或其他引用,阻止人們復制對象仍然是有用的。 也許該類包含大量數據,並且復制過於重量級的操作。

您可能希望使用復制構造函數實現該類的某些方法,但不要將其暴露在類之外。 那么你把它變成私有的。 像任何其他方法一樣。

虛擬構造函數習慣用法 ”是一個需要私有或受保護的拷貝構造函數的重要案例。 在C ++中出現了一個問題,在這個問題中,您將獲得指向基類的指針,該對象實際上是從該基類繼承的,並且您想要復制它。 調用復制構造函數不會調用繼承類的復制構造函數,而是實際調用基類的復制構造函數。

注意:

class Base {

public:
   Base( const Base & ref ){ std::cout << "Base copy constructor" ; }
};

class Derived : public Base {

public:
   Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; }
}

Base * obj = new Derived;
Base * obj2 = new Derived(*obj);

上面的代碼會產生輸出:

"Base copy constructor"

這顯然不是程序員想要的行為! 程序員試圖復制“Derived”類型的對象,而是返回“Base”類型的對象!

通過使用上述習語來糾正該問題。 觀察上面寫的例子,重寫使用這個成語:

class Base {

public:
  virtual Base * clone () const = 0; //this will need to be implemented by derived class

protected:
   Base( const Base & ref ){ std::cout << "Base copy constructor" ; }
};

class Derived : public Base {

public:
  virtual Base * clone () const {

    //call private copy constructor of class "Derived"
    return static_cast<Base *>( new Derived(*this) );
  }

//private copy constructor:
private:
   Derived( const Derived & ref ) : Base(ref) { std::cout << "Derived copy constructor"; }
}

Base * obj = new Derived;
Base * obj2 = obj->clone();

上面的代碼會產生輸出:

"Base copy constructor"
"Derived copy constructor"

換句話說,用所需類型“Derived”構造的對象,而不是“Base”類型!

正如您所看到的,在Derived類型中,復制構造函數是故意設置為私有的,因為讓程序員無意中嘗試手動調用復制構造函數而不是使用clone提供的聰明接口是不好的API設計( )。 換句話說,可直接調用的公共拷貝構造函數可能會導致程序員犯下第1部分中提到的錯誤。在這種情況下,最佳實踐是隱藏復制構造函數,並且只能通過使用“clone”方法間接訪問)”。

暫無
暫無

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

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