4

私は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なんで?

4

3 に答える 3

4

GameBoard コピー コンストラクターを明示的に呼び出さないと、既定のコンストラクターが呼び出されるのはなぜですか?

の明示的なコピー コンストラクターを記述していない場合Game、コンパイラはコピー コンストラクトを生成しますgb。一方、独自のコピーコンストラクターを明示的に定義した瞬間、コンパイラーは「OK、この男は自分が何をしているかを本当に知っているので、彼に完全な制御を与えましょう」と考えます。

完全な制御を取得することで、コピーの作成中に実行するすべてのアクションを正確に指定する責任もあります。アクションを指定しない場合、コンパイラは、オブジェクトを構築するためのデフォルトのメカニズムが採用されると想定する必要があります。また、オブジェクトを構築するためのデフォルトのメカニズムは、デフォルトのコンストラクタを呼び出すことです。

あなたが何をしているのかを知っているように見えるので、コンパイラはメンバー変数の正しい構築手順について何も仮定しません。あなたは完全にコントロールしたかったのですが、今それを手に入れました:何かを忘れましたか? デフォルトの構築が得られます。

GameBoard 代入演算子を明示的に呼び出さないと、代入は行われません。なんで?

答えはかなり似ています。独自の を明示的に定義することoperator =で、オブジェクトの割り当て方法を完全に制御したいことをコンパイラに伝えます。コンパイラは、正しくない可能性のある仮定を作成して、あなたのやり方に乗ろうとはしません。代わりに、それは脇に置いて、すべての決定権をあなたに任せます.

一方、コンパイラが代入を暗黙のうちに生成したが、なんらかの (うまくgb = g.gbいけば良い) 理由でその代入を実行したくないとします。デフォルトの動作がそんな指示?

また、本当に必要な場合を除き、コンパイラにクラスのコピー/移動コンストラクタ、デストラクタ、およびコピー/移動代入演算子を暗黙的に生成させることをお勧めします。詳細については、R. Martinho によるこの記事を参照してください。いわゆるゼロのルールに関するフェルナンデス。

これが明確にするのに役立つことを願っています。

于 2013-02-24T17:25:58.307 に答える
0

これらのコメントは、私にはあまり意味がありません。

コードを次のように単純化すると、次のようになります。

#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()

おそらく彼は、独自のコンストラクター/代入演算子を定義すると、デフォルトで発生することはデフォルトでは発生しないという事実について話しているのでしょう。もしそうなら、それは本当です (そして、同じ理由で混乱しているのかもしれません: これは非常に明白なことを説明しているように見えるからです)。

于 2013-02-24T17:29:27.913 に答える
0

コピー コンストラクターや代入演算子をカスタマイズする場合、すべてのメンバー変数を処理する必要があります。

コピー コンストラクターおよび/または代入演算子をコーディングしない場合、既定のものはコンパイラによって生成されます。デフォルトでは浅い代入とコピーが行われることに注意してください。どちらも各メンバー変数を調べて、代入演算子またはコピー コンストラクターを使用するだけです。

// 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.
}
于 2013-02-24T17:34:51.007 に答える