为什么c++的初始化列表在大括号之前

why C++ Initialization list is before brace?

本文关键字:c++ 初始化 列表 为什么      更新时间:2023-10-16

我想知道下面两个类有什么不同

示例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是通过的,最后一个是错误的?由于

在示例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
    }
}

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

第二个例子在c++中是无效的。如果你写的是name = name_,那么这就是一个正常的赋值。当然,这并不总是可能的;对象可以是const,也可以没有定义赋值操作符。这种方法也可能比第一个示例效率低,因为对象是默认初始化的和分配的

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

语言就是这样定义的。成员初始化式应该放在构造函数体之前。

在第一个示例中,使用以char *为参数的ctr初始化成员名

在第二种情况下,首先使用默认的ctr初始化,然后通过赋值操作符(operator=)获取值。这就是为什么你的情况是错误的,它已经在那里构造了,所以你不能再次使用ctr,你可以使用赋值操作符。

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

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之间存在歧义。

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

这样的字段必须精确初始化一次(由于它们的const-ness)。如果c++没有初始化列表,那么一个actor将看起来像这样:

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;
}

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