繁体   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