为什么C ++初始化列表在大括号之前?

时间:2011-05-02 11:13:13

标签: c++ initialization-list

我想知道以下两个课程有什么区别。

示例1:

class A
{
string name;
public:
  A(const char* _name):name(_name){}
  void print(){cout<<"A's name:"<<name<<endl;}
};

示例2:

class A
{
string name;
public:
  A(const char* _name){name(_name);}
  void print(){cout<<"A's name:"<<name<<endl;}}

为什么传递示例1而最后一个错误? 感谢

6 个答案:

答案 0 :(得分:3)

第一个示例是实际初始化。它具有许多优点,包括成为const成员的唯一方法,并具有适当的异常安全性。

第二个例子不是有效的C ++,AFAIK。如果您改为编写name = name_,那么这只是正常的分配。当然,这并不总是可行的;对象可能是const,或者没有定义赋值运算符。这种方法的效率也可能低于第一个示例,因为该对象都是默认初始化的

至于为什么初始化列表在构造函数体之前;好吧,这就是语言的定义方式。

答案 1 :(得分:3)

在示例1中,您立即使用给定值初始化字符串。 在示例2中,您首先创建一个空字符串,然后再将其分配。

尽管存在一些性能差异,并且由于复制构造函数处理等原因而忽略了可能的差异,但它基本上是相同的结果。

但是,一旦您使用const成员,您将不得不使用示例1的方式来执行此操作,例如我通常通过以下方式创建唯一ID:

class SomeObject
{
    static unsigned int nextID = 0;
    const unsigned int ID;
    SomeObject() : ID(nextID++)
    {
        // you can't change ID here anymore due to it being const
    }
}

答案 2 :(得分:2)

这就是语言的定义方式。成员初始值设定项应放在构造函数的主体之前。

答案 3 :(得分:1)

在第一个示例中,使用ctr获取char *作为参数初始化成员名称。

在第二种情况下,它首先使用默认ctr进行初始化,然后由赋值运算符(operator =)获取值。这就是为什么你的情况已经构造错误,所以你不能再次使用ctr你只能使用赋值运算符。

答案 4 :(得分:0)

原因是名称查找在初始化列表和函数体中的工作方式不同:

class A
{
  std::string foo; // member name
public:
  A(const char* foo) // argument name
    : foo(foo) // member foo, followed by argument foo.
  {
    std::cout << foo; // argument foo.
  }
};

如果初始化列表在函数体内,则成员foo和参数foo之间会有歧义。

答案 5 :(得分:0)

初始化列表背后的动机是由于const字段按值保存对象(而不是引用/指针字段)。

这些字段必须初始化一次(由于它们的常量)。如果C ++没有初始化列表,那么ctor看起来就像:

class A {
public:
  const string s;
  const string t;

  A() {
    // Access to either s or t is not allowed - they weren't initialized
    s = "some-string";
    // now you can access s but you can't access t
    f(*this);

    t = "some other string";
    // From now you can access both ...
  }
}


void f(A& a) {
  // Can you access a.s or a.t?
  cout << a.s << a.t;
}

如果没有初始化列表,ctor可以将类型为A的部分初始化对象传递给函数,并且该函数无法知道哪些字段已初始化。编译器/链接器检查风险太大且非常困难。