KKO*_*nge 30 c++ initialization this
在我写的一个小游戏中,我有一个Weapon带有两个构造函数的类,一个用于获取一些参数来生成一个自定义武器,另一个用于获取一个默认武器CHAIN_GUN:
Weapon::Weapon (void) {
// Standard weapon
*this = getWeapon(CHAIN_GUN);
return;
}
Run Code Online (Sandbox Code Playgroud)
问题:使用*this和operator=初始化课程是否会产生任何负面影响?
Fil*_*efp 33
想象一下,有人要求你画一幅画......你会吗?
- 先画出你的默认(第一个)(你熟悉的那个熟悉的笑脸),
- 然后画出有人要求的东西(第二),
- 只是再画一次同样的东西,但是在包含默认值的画布上,
- 然后燃烧第二幅画?
这篇文章将试图解释为什么这个明喻是相关的.
为什么这是一个不好的想法?
我从未见过使用赋值运算符实现的默认构造函数,老实说,这不是我推荐的,也不是代码审查期间的支持.
这样的代码的主要问题是,根据定义,我们构造两个对象(而不是一个)并调用一个成员函数,这意味着我们构造所有成员两次,然后必须复制/移动初始化所有成员调用赋值运算符.
在请求构造1个对象时,我们构造了2个对象,只是稍后将值从第2个复制到第1个并丢弃第2 个,这是不直观的.
结论:不要这样做.
(注意:在Weapon有基类的情况下会更糟)
(注意:另一个潜在的危险是工厂函数意外地使用default-constructor,导致在编译期间没有捕获到无限递归,如@Tychet Freat所述)
建议的解决方案
在您的特定情况下,您最好在构造函数中使用default-argument,如下例所示.
class Weapon {
public:
Weapon(WeaponType w_type = CHAIN_GUN);
...
}
Run Code Online (Sandbox Code Playgroud)
Weapon w1; // w_type = CHAIN_GUN
Weapon w2 (KNOWLEDGE); // the most powerful weapon
Run Code Online (Sandbox Code Playgroud)
(注意:上面的替代方法是使用C++ 11中提供的委托构造函数)
Vau*_*ato 16
使用赋值运算符来实现构造函数很少是个好主意.例如,在您的情况下,您可以使用默认参数:
Weapon::Weapon(GunType g = CHAIN_GUN)
: // Initialize members based on g
{
}
Run Code Online (Sandbox Code Playgroud)
在其他情况下,您可以使用委托构造函数(使用C++ 11或更高版本):
Weapon::Weapon(GunType g)
: // Initialize members based on g
{
}
Weapon::Weapon()
: Weapon(CHAIN_GUN) // Delegate to other constructor
{
}
Run Code Online (Sandbox Code Playgroud)
要记住的一件事是,如果operator=- 或者它调用的任何函数 - 是virtual,则不会调用派生类版本.这可能会导致未初始化的字段和以后的未定义行为,但这一切都取决于您的数据成员.
更一般地说,如果你的基础和数据成员有构造函数或出现在初始化列表中(或者在类声明中指定了C++ 11),那么它们的基础和数据成员都会被保证初始化 - 所以除了virtual上面的问题之外,operator=通常都会工作没有未定义的行为.
如果在operator=()调用之前已初始化基数或成员,则在使用之前初始值将被覆盖,优化器可能会或可能不会删除第一次初始化.例如:
std::string s_;
Q* p_;
int i_;
X(const X& rhs)
: p_(nullptr) // have to initialise as operator= will delete
{
// s_ is default initialised then assigned - optimiser may help
// i_ not initialised but if operator= sets without reading, all's good
*this = rhs;
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,它有点容易出错,即使你说得对,后来更新的人operator=可能也不会检查使用它的构造函数(ab)....
如果getWeapon()使用Prototype或Flyweight模式并尝试复制构造Weapon它返回,最终会导致无限递归导致堆栈溢出.
退后一步,问题是为什么getWeapon(CHAIN_GUN);存在这种形式.如果我们需要一个基于武器类型创建武器的函数,那么Weapon(Weapon_Type);构造函数似乎是一个合理的选择.也就是说,有一些罕见但丰富的边缘情况getWeapon可能会返回除了一个Weapon永远不会被分配给a 的对象以外的东西Weapon,或者可能因为构建/部署原因而保持独立....