我什么时候必须使用初始化列表来初始化C ++类成员?

时间:2009-10-27 17:41:05

标签: c++ stl map initialization

让我说我有     std::map< std::string, std::string > m_someMap作为A类的私有成员变量

两个问题:(我问的唯一原因是因为我遇到了类似的代码)

  1. 这一行的目的是什么:

    A::A() : m_someMap()
    

    现在我知道这是初始化,但你必须这样做吗? 我很困惑。

  2. std::map< std::string, std::string > m_someMap的默认值是什么,C#还定义int,double等始终初始化为defualt 0且对象为null(至少在大多数情况下) 那么C ++中的规则是什么?是对象初始化为defualt为null和原语为垃圾? 当然我正在考虑实例变量。

  3. 编辑:

    另外,既然大多数人都指出这是一种风格选择而不是必要的,那么:

    A :: A():m_someMap(),m_someint(0),m_somebool(false)

7 个答案:

答案 0 :(得分:12)

<强> m_somemap

  1. 你不必。
  2. 如果省略它会得到什么:空std::map< std::string, std::string >,即该地图中没有元素的有效实例。
  3. <强> m_somebool

    1. 如果您希望它具有已知值,则必须将其初始化为truefalse。布尔值是“普通的旧数据类型”,它们没有构造函数的概念。此外,C ++语言没有为非显式初始化的布尔值指定默认值。
    2. 如果省略它会得到什么:具有未指定值的布尔成员。您不能这样做,以后再使用它的值。因此,强烈建议您初始化此类型的所有值。
    3. <强> m_someint

      1. 如果您希望它具有已知值,则必须将其初始化为某个整数值。整数是“普通旧数据类型”,它们没有构造函数的概念。此外,C ++语言没有为非显式初始化的整数指定默认值。
      2. 如果省略它会得到什么:具有未指定值的int成员。您不能这样做,以后再使用它的值。因此,强烈建议您初始化此类型的所有值。

答案 1 :(得分:4)

没有必要真正这样做 默认构造函数将自动执行此操作。

但有时通过明确表示它可以作为一种文档:

class X
{
    std::map<string,string>  data;
    Y                        somePropertyOfdata;

    X()
      :data()                    // Technically not needed
      ,somePropertyOfdata(data)  // But it documents that data is finished construction
    {}                           // before it is used here.
};

C ++中的规则是,除非你明确地初始化POD数据,否则它是未定义的,而其他类有自动调用的默认构造函数(即使程序员没有明确地这样做)。

但是这样说。考虑一下:

template<typename T>
class Z
{
     T  data;   
     Z()
        :data()    // Technicall not need as default constructor will
                   // always be called for classes.
                   // But doing this will initialize POD data correctly
                   // if T is a basic POD type. 
     {}
};

在这里,您会将数据视为默认初始化 技术上POD没有构造函数所以如果T是int那么你会期望它做什么吗?因为它是显式初始化它被设置为0或POD类型的等价物。

编辑:

class A
{
    std::map<string,string>   m_someMap;
    int                       m_someint;
    bool                      m_somebool;
   public:
    A::A()
       : m_someMap()      // Class will always be initialised (so optional)
       , m_someint(0)     // without this POD will be undefined
       , m_somebool(false)// without this POD will be undefined
    {}
};

答案 2 :(得分:1)

正如其他人所指出的那样:没有必要,但或多或​​少是一种风格问题。 好处:它表明您明确希望使用默认构造函数并使您的代码更加冗长。缺点:如果你有多个ctor,维护所有这些变更可能会很痛苦,有时你会添加类成员而忘记将它们添加到ctors初始化列表并使它看起来不一致。

答案 3 :(得分:1)

A::A() : m_someMap()

在这种情况下,此行是不必要的。但是,一般,它是初始化类成员的唯一正确方法。

如果您有这样的构造函数:

X() : y(z) {
 w = 42;
}

然后在调用X构造函数时发生以下情况:

  • 首先,所有成员都被初始化:对于y,我们明确地说我们希望调用以z为参数的构造函数。对于w,会发生什么取决于w的类型。如果w是POD类型(即,基本上是C兼容类型:没有继承,没有构造函数或析构函数,所有成员都是公共的,并且所有成员都是POD类型),那么它是 not 已初始化。它的初始值是在该内存地址找到的任何垃圾。如果w是非POD类型,则调用其默认构造函数(非POD类型始终在构造时初始化)。
  • 一旦构建了两个成员,我们然后调用赋值运算符以将42分配给w

需要注意的重要一点是,在进入构造函数体之前,所有构造函数都被称为。一旦我们进入体内,所有成员都已经初始化。 因此构造函数体有两个可能存在的问题。

  • 如果w的类型没有默认构造函数,该怎么办?然后这将无法编译。然后必须:之后显式初始化,就像y一样。
  • 如果调用这两个默认构造函数和赋值运算符的序列不必要地慢了怎么办?也许只是简单地调用正确的构造函数会更有效率。

简而言之,由于m_someMap是非POD类型,我们严格来说并不需要: m_someMap()。它无论如何都是默认构建的。但如果它是POD类型,或者我们想要调用另一个构造函数而不是默认构造函数,那么我们就需要这样做。

答案 4 :(得分:0)

只是要明确发生了什么(关于你的第二个问题)

std::map< std::string, std::string > m_someMap创建一个名为m_someMap的堆栈变量,并在其上调用默认构造函数。所有对象的C ++规则都是:

T varName;

其中T是一个类型,varName是默认构造的。

T* varName;

应该在新标准中明确赋值为NULL(或0) - 或nullptr。

答案 5 :(得分:0)

澄清默认值问题:

C ++没有某些类型隐含的概念被引用。除非将某些内容显式声明为指针,否则不能采用空值。这意味着每个类都有一个默认构造函数,用于在未指定构造函数参数时构建初始值。如果没有声明默认构造函数,编译器将为您生成一个。此外,每当一个类包含属于类别的成员时,这些成员将在对象构造时通过它们自己的默认构造函数隐式初始化,除非使用冒号语法显式调用另一个构造函数。

恰好所有STL容器类型的默认构造函数都构建了一个空容器。其他类可能有其默认构造函数的其他约定,因此您仍然需要知道它们是在这样的情况下调用的。这就是A::A() : m_someMap()行的原因,它实际上只是告诉编译器做它本来会做的事情。

答案 6 :(得分:0)

在C ++中创建对象时,构造函数将按以下顺序进行:

  1. 调用整个类树中的所有父虚拟类的构造函数(以任意顺序)
  2. 按声明的顺序调用所有直接继承的父类的构造函数
  3. 按声明的顺序调用所有成员变量的构造函数
  4. 还有一些比这更具体的细节,有些编译器允许你强制执行这个特定顺序的一些事情,但这是一般的想法。对于每个构造函数调用,您都可以指定构造函数参数,在这种情况下,C ++将按指定的方式调用构造函数,或者您可以不管它,C ++将尝试调用默认构造函数。默认构造函数就是不带参数的构造函数。

    如果您的任何虚拟父类,非虚拟父类或成员变量没有默认构造函数或需要使用非默认构造函数创建,则将它们添加到构造函数调用列表中。因为C ++假设一个默认的构造函数调用,所以在列表中放置一个默认构造函数并完全不让它完全没有区别(C ++不会(除非在这个问题范围之外的特殊情况下)创建一个对象而不调用一个某种构造函数)。如果某个类没有默认构造函数而您没有提供构造函数调用,则编译器将抛出错误。

    当涉及内置类型floatint时,默认构造函数根本不执行任何操作,因此变量将具有内存中剩余的任何内容的默认值。所有内置类型都有一个复制构造函数,因此您可以通过将其初始值作为变量构造函数的唯一参数传递来初始化它们。