簡體   English   中英

具有用戶定義的類成員的類的復制構造函數

[英]copy constructor of a class which has an user-defined class member

我正在閱讀 C++ 第 14 章中的思考:“不會自動繼承的函數”

class GameBoard {
public:
  GameBoard() { cout << "GameBoard()\n"; }
  GameBoard(const GameBoard&) { 
    cout << "GameBoard(const GameBoard&)\n"; 
  }
  GameBoard& operator=(const GameBoard&) {
    cout << "GameBoard::operator=()\n";
    return *this;
  }
  ~GameBoard() { cout << "~GameBoard()\n"; }
};

class Game {
  GameBoard gb; // Composition
public:
  // Default GameBoard constructor called:
  Game() { cout << "Game()\n"; }
  // You must explicitly call the GameBoard
  // copy-constructor or the default constructor
  // is automatically called instead:
  Game(const Game& g) : gb(g.gb) { 
  //Game(const Game& g) {
    cout << "Game(const Game&)\n"; 
  }
  Game(int) { cout << "Game(int)\n"; }
  Game& operator=(const Game& g) {
    // You must explicitly call the GameBoard
    // assignment operator or no assignment at 
    // all happens for gb!
    gb = g.gb;
    cout << "Game::operator=()\n";
    return *this;
  }
  class Other {}; // Nested class
  // Automatic type conversion:
  operator Other() const {
    cout << "Game::operator Other()\n";
    return Other();
  }
  ~Game() { cout << "~Game()\n"; }
};

在上面的代碼中,我被Game類的復制構造函數和賦值構造函數弄糊塗了:

      // You must explicitly call the GameBoard
      // copy-constructor or the default constructor
      // is automatically called instead:
      Game(const Game& g) : gb(g.gb) { 
      //Game(const Game& g) {
        cout << "Game(const Game&)\n"; 
      }

      Game& operator=(const Game& g) {
        // You must explicitly call the GameBoard
        // assignment operator or no assignment at 
        // all happens for gb!
        gb = g.gb;
        cout << "Game::operator=()\n";
        return *this;
      }

作者給出評論:“你必須顯式調用GameBoard復制構造函數,否則會自動調用默認構造函數: ”為什么如果我不顯式調用GameBoard復制構造函數,則會調用默認構造函數?

對於賦值構造函數,如果我沒有顯式調用GameBoard賦值運算符,則不會發生賦值。 為什么?

為什么如果我沒有顯式調用 GameBoard 復制構造函數,那么會調用默認構造函數?

如果您沒有為Game編寫任何顯式的復制構造函數,那么編譯器將為您生成一個復制構造gb的函數。 另一方面,在您明確定義自己的復制構造函數的那一刻,編譯器會認為“好吧,這家伙真的知道他在做什么,所以讓我們完全控制他吧”

通過獲得完全控制權,您還有責任明確指定在復制構建期間要執行的所有操作。 如果您不指定任何操作,則編譯器必須假定應采用構造對象的默認機制。 而構造對象的默認機制是調用默認構造函數

由於您似乎知道自己在做什么,因此編譯器不會對您的成員變量的正確構造過程做出任何假設。 您想要完全控制,現在您擁有它:忘記了什么? 您將獲得默認構造。

如果我沒有明確調用 GameBoard 賦值運算符,則不會發生賦值。 為什么?

答案非常相似。 通過顯式定義自己的operator = ,您就是在告訴編譯器您希望完全控制對象的分配方式。 編譯器不會通過做出可能不正確的假設來嘗試繼續前進。 相反,它只會擱置一旁,留給你所有的決策權。

另一方面,假設編譯默默地生成了賦值gb = g.gb ,但是由於某些(希望是好的)原因,您希望執行該賦值:如果默認行為是生成這樣的指令?

此外,除非確實需要,否則讓編譯器為您的類隱式生成復制/移動構造函數、析構函數和復制/移動賦值運算符是一種很好的編程習慣:有關更多信息,請參閱 R. Martinho 的這篇文章費爾南德斯關於所謂的零規則

希望這有助於澄清。

這些評論對我來說真的沒有意義。

如果您將代碼簡化為以下內容:

#include <iostream>

using namespace std;

class GameBoard {
public:
  GameBoard() { cout << "GameBoard()\n"; }
  GameBoard(const GameBoard&) { 
    cout << "GameBoard(const GameBoard&)\n"; 
  }
  GameBoard& operator=(const GameBoard&) {
    cout << "GameBoard::operator=()\n";
    return *this;
  }
  ~GameBoard() { cout << "~GameBoard()\n"; }
};

class Game {
  GameBoard gb; // Composition
};

int main() { 
    cout << "Default Constructing Game object\n";
    Game g;

    cout << "\nCopy constructing Game object\n";
    Game h = g; // uses copy ctor

    cout << "\nDefault constructing another Game object\n";
    Game i;

    cout << "\nAssigning a Game object\n";
    i = g;  // uses assignment operator.
    return 0;
}

您將(或無論如何應該)得到如下輸出:

Default Constructing Game object
GameBoard()

Copy constructing Game object
GameBoard(const GameBoard&)

Default constructing another Game object
GameBoard()

Assigning a Game object
GameBoard::operator=()
~GameBoard()
~GameBoard()
~GameBoard()

也許他在談論這樣一個事實,即當您定義自己的構造函數/賦值運算符時,默認情況下不會發生默認情況。 如果是這樣,那是真的(也許我們出於同樣的原因感到困惑:因為這似乎是在為異常明顯的事情而苦惱)。

當您自定義復制構造函數和/或賦值運算符時,您必須處理所有成員變量。

當您不對復制構造函數和/或賦值運算符進行編碼時,編譯器會生成默認值。 請注意,默認值執行淺分配和復制。 兩者都只是遍歷每個成員變量並使用它們的賦值運算符或復制構造函數。

// NOTE don't you strdup or free in c++ (see new, delete and the string class)
// this is for BAD example only
class Stuff {
public:
   char * thing;
   Stuff( void ) : thing(0) {}
   ~Stuff() { if( thing ) free thing; };
   void copyThing( char * other ) { thing = strdup(other); }

}

class Other {
public:
    Stuff   myStuff;
}

void BadExample() {
    Other oa;
    oa.copyThing( "Junk" );
    Other ob;
    ob = oa; // will work but oa and ob myStuff.thing will point to the same address
             // and you will attempt to free it twice during deconstruction
}

class BetterStuff {
public:
   char * thing;
   BetterStuff( void ) : thing(0) {}
   ~BetterStuff() { if( thing ) free thing; };
   void copyThing( char * other ) { thing = strdup(other); }
   BetterStuff & operator = ( const BetterStuff & rhs ) {
     if( rhs.thing ) thing = strdup( rhs.thing );
   }
}


class BetterOther {
public:
    BetterStuff   myStuff;
}


void Example() {
    BetterOther oa;
    oa.copyThing( "Junk" );
    BetterOther ob;

    ob = oa; // now ob has a private copy of the string and can free it.
}

暫無
暫無

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

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