【问题标题】:Why can const members be modified in a constructor?为什么可以在构造函数中修改 const 成员?
【发布时间】:2018-09-06 15:11:47
【问题描述】:

我很好奇为什么可以在构造函数中修改 const 成员。

初始化中是否有任何标准规则可以覆盖成员的“常量”?

struct Bar {
    const int b = 5; // default member initialization
    Bar(int c):b(c) {}
};

Bar *b = new Bar(2); // Problem: Bar::b is modified to 2
                     // was expecting it to be an error

有什么想法吗?

【问题讨论】:

  • ...您还能如何将值分配给const 成员...?另外,关于其他 cmets,所以离题很痛苦。

标签: c++ c++11 initialization c++14 constants


【解决方案1】:

这不是修改(或分配),而是initialization。例如

struct Bar {
    const int b = 5; // initialization (via default member initializer)
    Bar(int c)
        :b(c)        // initialization (via member initializer list)
    {
        b = c;       // assignment; which is not allowed
    }
};

const 数据成员无法修改或分配,但可以(并且需要)通过成员初始化器列表或默认成员初始化器进行初始化。

如果在同一个数据成员上同时提供了默认成员初始化器和成员初始化器,则默认成员初始化器将被忽略。这就是 b->b 被初始化为值 2 的原因。

如果一个成员有一个默认的成员初始化器并且还出现在构造函数的成员初始化列表中,那么默认的成员初始化器将被忽略。

另一方面,默认成员初始化器只有在成员初始化器列表中没有指定数据成员时才会生效。例如

struct Bar {
    const int b = 5;   // default member initialization
    Bar(int c):b(c) {} // b is initialized with c
    Bar() {}           // b is initialized with 5
};

【讨论】:

  • 只是为了扩展“不是修改(或赋值)”的声明:Bar(int c):b(c) {} 在第一次构造时初始化 b,但 Bar(int c) { b = c; } 将在构造后分配它其默认值为 5。这是 const 所阻止的。
【解决方案2】:

除了songyuanya's great answer,如果你想要一个无法在构造函数中初始化的const数据成员,你可以使成员static:

struct Bar {
    static const int b = 5; // static member initialization
    Bar(int c)
        :b(c)        // Error: static data member can only be initialized at its definition
    {
        b = c;       // Error: b is read-only
    }
};

在 C++17 中,您可以通过将其设为 inline 来进一步改进:

struct Bar {
    inline static const int b = 5; // static member initialization
    Bar(int c)
        :b(c)        // Error: static data member can only be initialized at its definition
    {
        b = c;       // Error: b is read-only
    }
};

这样您就不会遇到 ODR 问题。

【讨论】:

    【解决方案3】:

    当你这样做时:

    struct Bar {
        const int b = 5; // default member initialization
        ...
    };
    

    您是在告诉编译器使用默认构造函数来执行此操作:

    ...
    Bar() : b(5) 
    {}
    ...
    

    无论您是否提供了默认构造函数。当您确实提供默认构造函数和初始赋值时,您会覆盖编译器的默认赋值代码(即b(5))。 当您有多个构造函数时,声明时的默认初始化/赋值很有用,您可能会也可能不会在所有构造函数中分配 const 成员:

    ...
    Bar() = default; // b=5
    Bar(int x) : b(x) // b=x
    Bar(double y) : /*other init, but not b*/  // b=5
    ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多