【问题标题】:why C++ Initialization list is before brace?为什么 C++ 初始化列表在大括号之前?
【发布时间】:2013-04-29 21:15:15
【问题描述】:

我想知道下面两个类有什么区别。

示例 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通过了,最后一个错了? 谢谢

【问题讨论】:

    标签: c++ initialization-list


    【解决方案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
        }
    }
    

    【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

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

          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 之间会有歧义。

          【讨论】:

          • 谢谢大家的回答,我明白了,它是在调用构造函数之前构造的,所以无法再次初始化,所以必须赋值。谢谢
          【解决方案6】:

          初始化列表背后的动机是由于 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 类型的部分初始化对象传递给函数,而该函数将无法知道哪些字段已初始化。编译器/链接器检查的风险太大且非常困难。

          【讨论】:

            猜你喜欢
            • 2021-12-31
            • 2018-03-06
            • 1970-01-01
            • 2012-11-21
            • 2016-12-11
            • 2023-03-11
            • 2012-05-04
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多