【问题标题】:What is the advantage of using initializers for a constructor in C++?在 C++ 中为构造函数使用初始化器有什么好处?
【发布时间】:2009-11-26 20:00:48
【问题描述】:
Sphere() : theRadius(1.0)
{
}

为什么使用初始化器编写的构造函数(上)比在其主体中初始化数据成员的构造函数(下)更好?

Sphere()
{
     theRadius = 1.0;
}

【问题讨论】:

    标签: c++


    【解决方案1】:

    所有成员在进入构造函数的主体之前被初始化。如果您没有在初始化列表中提供初始化程序,那么它们是默认构造的。

    在您的第一个示例中发生以下情况:

    • theRadius 初始化为 1.0

    在您的第二个示例中发生以下情况:

    • theRadius 已初始化
    • 1.0 复制到theRadius

    对于像整数和浮点数这样的简单类型,这并不重要。但是,如果您的成员是具有非平凡构造函数/复制构造函数的对象,那么使用初始化列表方法会更有效。

    【讨论】:

    • 答案很好,但我还要提到当前 C++ 中的初始化程序也有缺点(我认为他们在 C++0x 中修复了它,您可以在其中调用其他构造函数)。如果您碰巧有具有多个构造函数的类,则最终可能会得到重复的初始化列表。调用一些常见的 init 函数可以修复这种重复,但您会失去效率。另一个值得一提的问题是(缺少)初始化顺序 - 在构造具有依赖关系的对象时,例如参考“父”对象进行初始化时,您必须注意它。
    • @MaR,如果我有多个构造函数,我可以忍受重复。也不乏初始化的顺序。初始化的顺序非常明确。变量总是按照它们在类定义中声明的顺序进行初始化。这是 C++ 标准规定的。我不记得章节号了,但它在 SO 的 asnwers 中被引用了很多次
    • @Glen - 这就是括号中“缺少”的原因:o)有一个订单,但你必须留意它(订单在类定义中(不同的地方!)它不是语法错误),而非初始化代码非常明确。
    • @MaR,我明白你现在在说什么了。然而,这句话“另一个值得一提的问题是(缺乏)初始化顺序”读起来好像你在说没有初始化顺序。把它放在括号里并不意味着你的意思
    【解决方案2】:

    如果您没有显式初始化字段,编译器将尝试运行它们的默认构造函数。如果它们没有可访问的默认构造函数,您的源代码将无法编译:

    class Field {
    public:
        Field(int x) {}
    };
    class Test {
        Field f;
    public: 
        Test() {  // compiler error here. `Field` doesn't have default constructor.
           f = Field(10); 
        } 
    };
    int main ( ) {
        Test t;
    }
    

    如果他们确实有一个默认构造函数,那么你调用它是不必要的,因为你正在调用正文中字段的另一个构造函数。

    【讨论】:

      【解决方案3】:

      在进入构造函数的主体之前,总是为对象属性调用构造函数。 如果自己不指定构造函数,会调用默认的,后面会用operator=改值。

      这可能不是您想要的。 特别是,您不能更改构造函数主体内的const 属性的值。 你必须使用初始化列表。

      【讨论】:

        【解决方案4】:

        因为否则会调用对象的默认构造函数,然后设置值。

        这只会调用字符串构造函数

        class Foo{
             std::string s;
             Foo() : s("Hello World"){}
        }
        

        在哪里:

        class Foo{
             std::string s;
             Foo(){
                 s = "Hello World";
             }
        }
        

        会调用默认的字符串构造函数,然后将字符串的值设置为“Hello World”

        【讨论】:

          【解决方案5】:

          如前所述,对于非原始类型,它会有所不同。

          这也是初始化 const 成员变量的唯一方法。

          class MyClass
          {
           const int x;
          public: 
           MyClass(int x)
           {
            this->x=x;
           }
          };
          

          这不应该编译。

          【讨论】:

            【解决方案6】:

            正如其他人所说,将第一种形式用于具有非平凡构造的类型会更有效。

            此外,第一种形式使您能够捕获使用 try/catch 块的函数形式初始化的成员对象(或基类)的构造函数中抛出的任何异常。例如:

            MyObject() : memberObject(...)
            try {
              // my constructor
            }
            catch (...) {
              // catches exceptions in try block AND memberObject constructor
            }
            

            这不是缺少一组括号。如果您确实在 try/catch 块周围放置了另一组括号,它将不会在 memberObject 构造函数中捕获异常,而只是在 try 块中。

            【讨论】:

              【解决方案7】:

              它主要用于初始化常量和引用变量,因为它们不能在构造函数体中初始化。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-06-30
                • 1970-01-01
                • 2013-06-16
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-03-21
                相关资源
                最近更新 更多