使用* this初始化一个类有什么后果吗?

时间:2014-06-03 05:42:24

标签: c++ initialization this

在我写的一个小游戏中,我有一个带有两个构造函数的类Weapon,一个用于获取一些参数来生成一个自定义武器,另一个用于获取一个默认武器({{} 1}}):

CHAIN_GUN

问题:使用Weapon::Weapon (void) { // Standard weapon *this = getWeapon(CHAIN_GUN); return; } *this初始化课程会产生任何负面影响吗?

5 个答案:

答案 0 :(得分:33)

  

想象一下,有人要求你画一幅画......你会不会;

     
      
  • 首先绘制你的默认 1st )(你非常喜欢那个熟悉的笑脸),
  •   
  • 然后绘制有人要求的内容( 2nd ),
  •   
  • 只是再次绘制相同的东西,但在包含默认的画布上,
  •   
  • 然后刻录第二次画?
  •   
     

这篇文章将试图解释为什么这个明喻是相关的。


为什么这是一个不好的想法?

我从未见过使用赋值运算符实现的默认构造函数,老实说,这不是我推荐的,也不支持代码审核

这样的代码的主要问题是,根据定义,我们构建两个对象(而不是一个)并调用成员函数,这意味着我们构造所有成员两次,后来必须通过调用赋值运算符来复制/移动初始化所​​有成员。

在请求构建 1 对象时,我们构造 2 对象,但稍后将值从 2nd 复制到 1st 并弃掉 2nd

结论不要这样做

注意:如果Weapon有基类,则会更糟糕)

注意:另一个潜在的危险是工厂函数意外地使用默认构造函数,导致在编译期间没有捕获到无限递归,如@ Ratchet Freat所示)


建议的解决方案

在您的特定情况下,您最好在构造函数中使用 default-argument ,如下例所示。

class Weapon {
public:
  Weapon(WeaponType w_type = CHAIN_GUN);
  ...
}

Weapon w1;             // w_type = CHAIN_GUN
Weapon w2 (KNOWLEDGE); // the most powerful weapon

注意:上述选择的另一种方法是使用{+ 3}},可在C ++ 11中使用)

答案 1 :(得分:16)

使用赋值运算符实现构造函数很少是个好主意。例如,在您的情况下,您可以使用默认参数:

Weapon::Weapon(GunType g = CHAIN_GUN)
: // Initialize members based on g
{
}

在其他情况下,您可以使用委托构造函数(使用C ++ 11或更高版本):

Weapon::Weapon(GunType g)
: // Initialize members based on g
{
}

Weapon::Weapon()
: Weapon(CHAIN_GUN) // Delegate to other constructor
{
}

答案 2 :(得分:7)

要记住的一件事是,如果operator= - 或它调用的任何函数 - 是virtual,则派生类版本 赢了 被调用。这可能会导致未初始化的字段和以后的未定义行为,但这一切都取决于您的数据成员。

更一般地说,如果你的base和数据成员有构造函数或者出现在初始化列表中(或者在类声明中赋值给C ++ 11),那么你的base和data成员都会被保证初始化 - 所以除了{{1在上面的问题中,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; } 的人可能也不会检查使用它的构造函数(ab)....

如果operator=使用Prototype或Flyweight模式并尝试复制构造它返回的getWeapon(),则最终会导致无限递归导致堆栈溢出。


退后一步,问题是Weapon存在于那种形式中的原因。如果我们需要一个基于武器类型创建武器的函数,那么getWeapon(CHAIN_GUN);构造函数似乎是一个合理的选择。也就是说,有一些罕见但丰富的边缘情况,Weapon(Weapon_Type);可能会返回除getWeapon对象以外的其他内容,而这些对象永远不会被分配给Weapon,或者可能会保持独立出于构建/部署原因....

答案 3 :(得分:3)

如果你定义了一个非复制=赋值运算符,它允许Weapon在构造之后改变它的类型,那么在赋值方面实现构造函数就可以了,并且是一个很好的方法集中初始化代码。但是如果Weapon并不意味着在构造之后改变类型,那么非复制=赋值运算符没有多大意义,更不用说用于初始化。

答案 4 :(得分:3)

我确定是的。

你已经在" getWeapon"中创建了对象。功能,然后你复制它,这可能是长时间的操作。所以,至少你必须尝试移动语义。

但是。 如果在里面" getWeapon"你打电话给构造函数(并且你做了,不知何故" getWeapon"必须创建你的类以将它返回到你的复制操作),你创建非常不清楚的架构,当一个构造函数调用调用另一个构造函数的函数时。

我相信您必须将参数初始化分离为私有函数,必须以您希望的方式从构造函数中调用它们。