复制具有用户定义的类成员的类的构造函数 我在C++中阅读思维第14章:“不自动继承的函数”< /P>
作者给出了评论:“您必须显式调用复制具有用户定义的类成员的类的构造函数 我在C++中阅读思维第14章:“不自动继承的函数”< /P>,c++,copy-constructor,composition,assignment-operator,C++,Copy Constructor,Composition,Assignment Operator,作者给出了评论:“您必须显式调用GameBoard复制构造函数,否则会自动调用默认构造函数:”为什么如果我不显式调用GameBoard复制构造函数,那么会调用默认构造函数 对于赋值构造函数,如果我没有显式调用GameBoard赋值操作符,那么就不会发生赋值操作。为什么 为什么如果我没有显式调用GameBoard复制构造函数,那么会调用默认构造函数 如果您没有为游戏编写任何显式的复制构造函数,那么编译器将为您生成一个复制构造函数gb。另一方面,当您明确定义自己的复制构造函数时,编译器认为“好吧,这
GameBoard
复制构造函数,否则会自动调用默认构造函数:”为什么如果我不显式调用GameBoard
复制构造函数,那么会调用默认构造函数
对于赋值构造函数,如果我没有显式调用GameBoard
赋值操作符,那么就不会发生赋值操作。为什么
为什么如果我没有显式调用GameBoard复制构造函数,那么会调用默认构造函数
如果您没有为游戏
编写任何显式的复制构造函数,那么编译器将为您生成一个复制构造函数gb
。另一方面,当您明确定义自己的复制构造函数时,编译器认为“好吧,这家伙真的知道他在做什么,所以让我们给他完全控制权”
通过获得完全控制权,您也有责任明确指定在副本构建过程中要执行的所有操作。如果未指定任何操作,则编译器必须假定应采用构造对象的默认机制。构造对象的默认机制是调用默认构造函数
由于您似乎知道自己在做什么,编译器不会对成员变量的正确构造过程做出任何假设。你想要完全控制,现在你有了:忘了什么?您将获得默认构造
如果我没有显式调用GameBoard赋值操作符,那么就不会发生赋值。为什么?
答案非常相似。通过明确定义自己的操作符=
,您告诉编译器您希望完全控制对象的分配方式。编译器不会试图通过做出可能不正确的假设来阻止您。相反,它只会搁置一边,留给你所有的决策权
另一方面,假设编译器以静默方式生成赋值gb=g.gb
,但出于某些(希望是好的)原因,您不希望执行该赋值:如果默认行为是生成这样的指令,您将如何告诉编译器不执行该赋值
此外,除非确实需要,否则让编译器为类隐式生成复制/移动构造函数、析构函数和复制/移动赋值运算符是一种很好的编程实践:有关更多信息,请参阅R.Martinho Fernandes撰写的这篇文章,文中介绍了所谓的
希望这有助于澄清。这些评论对我来说真的没有意义 如果将代码简化为以下内容:
#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.
}
我相信作者的意思是,如果您不放置
gb(g.gb)
部分,gb
将使用默认构造函数来构造。它不是赋值构造函数(因为它不是构造函数)。正如书中所说,它被称为赋值运算符。通常,在这样一个简单的情况下,您根本不会实现这两个函数——在这种情况下,它们会自动为您复制或分配gb。如果您确实显式地实现它们,就像在这里将日志记录到cout所做的那样,那么您需要显式地复制默认行为。+1,但是,我想知道,如果编译器没有被编程为以人类语言的形式思考无关的思想,是否会更快。@BenjaminLindley:可能是这样,是的:-)还有一个问题,为什么不显式调用GameBoard
assignment操作符时不调用默认构造函数?@FihopZz:assignment不是构造。指定对象时,没有要构造的内容。
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.
}