0

我已经了解了什么是初始化列表以及如何使用它,但我仍然想知道一件事。

如果有的话,使用初始化列表初始化类的变量或在构造函数中自己进行初始化有什么区别?

例如

class MyClass {
public:

    MyClass() : m_classID(-1), m_userdata(0) { 
    }

    int m_classID;
    void *m_userdata;
};

VS

class MyClass {
public:

    MyClass(){
        m_classID = -1;
        m_userdata = 0;
    }

    int m_classID;
    void *m_userdata;
};

它只是一种使代码更易于阅读和编写的设计习惯,还是有其他特定原因?

4

6 回答 6

3

如果这些变量是基本类型,并且它们const不是引用,那么就没有区别。

如果您的成员是引用或它是const,那么您将不得不初始化它们,并且只能通过 in 初始化列表来完成,原因与禁止您创建const基本类型的未初始化引用或变量的原因相同:

int& x; // ERROR!
const int y; // ERROR!

这些对象在初始化后不能重新分配:因此,它们必须进行值初始化是有道理的,并且不允许您在此类未初始化的成员变量的情况下执行类的构造函数的主体。

因此,当您确实有选择时,对于基本类型,两种形式之间没有区别 - 请记住,有些情况您没有选择。

另一方面,如果您的成员变量是类类型,那么不同之处在于,如果没有初始化列表,它们将被默认构造(如果可能的话),然后在构造函数的主体中分配;使用初始化列表,它们将直接使用您指定的参数构造。

请注意,C++11 为您提供了另一种可能性:

class MyClass {
public:
    int m_classID = -1;
    void *m_userdata = nullptr;
};
于 2013-03-10T22:08:09.047 回答
2

初始化列表允许您调用成员变量的构造函数。

如果在构造函数的主体中进行初始化,则调用的是operator=(赋值),而不是构造函数。

区别类似于这两种形式之间的区别:

int three(3);            // Constructor argument
int three; three = 3;    // Assignment after construction

这对于没有默认构造函数的成员(即没有参数的构造函数)和在创建时必须设置值的成员(如引用和常量)来说是不同的。

因此,只能在构造函数主体中初始化某些种类的成员;但是所有种类的成员都可以在初始化列表中初始化。这一点,以及没有为初始化列表初始化的成员调用默认构造函数的事实(这对复杂对象产生了影响,但对于整数来说并不重要),这意味着通常最好使用初始化器列表,给出选择。

于 2013-03-10T22:08:49.717 回答
1

对于您给出的情况,没有直接区别 - 初始化列表实际初始化的顺序略有不同(声明成员的顺序),在第二个示例中,如果您要交换行m_classID=-1说,m_userdata = 0他们确实会以相反的顺序初始化。

您不能在初始化列表之外初始化引用或 const 值,因此:

class bleh
{
   int& m_x;
   bleh(int x)
   {
      m_x = x;
   }
};

会给你一些错误。

class bleh
{
   int& m_x;
   bleh(int x): m_x(x)
   {
   }
};

是正确的方法。

于 2013-03-10T22:10:51.693 回答
1

只是指出c ++ 11中的一个区别:

class MyClass {
public:
    MyClass(std::string str) : m_str(str) { }
private:
    std::string m_str;
};

相当于做

class MyClass {
public:
    MyClass(std::string str) { m_str = std::move(str); }
private:
    std::string m_str;
};

然而:

class MyClass {
public:
    MyClass(std::string str) { m_str = str; }
private:
    std::string m_str;
};

只是调用赋值运算符。

我遇到了这个问题,因为我在 Lua 中初始化对象,当它们被破坏时,字符串被清理了两次,导致了一个信号陷阱,因为 Lua 认为它控制了传入字符串的所有权,但是一旦它被移动,就不再是案子。

我知道这并不能完全回答这个问题,但其他答案并没有直接解决这个问题。

于 2014-01-14T14:47:49.687 回答
0

有些东西没有 MIL 就无法初始化,例如引用成员。

class MyClass {
public:

    MyClass(MyOtherClass& otherClass)
         : m_classID(-1)
         , m_userdata(otherClass)
    { 
    }

    int m_classID;
    MyOtherClass& m_userdata;
};

此外,它是 intizlize const 成员的唯一方法。

于 2013-03-10T22:08:27.363 回答
0

初始化列表更可取,因为

  1. 您可以将它们与参考一起使用
  2. 您避免运行默认构造函数 + operator=,只是一个更快的 neccecary 构造函数。如果默认构造函数是私有的,则必须使用初始化列表
于 2013-03-10T22:12:19.490 回答