使用 class 字段作为参数时,委派构造函数会出现分段错误

Delegating constructor gives segmentation fault when using class field for argument

实际上分段错误发生在我试图编译的另一个程序中,这是由于这种行为而发生的。

我的问题是:

This is a bug or my fault?

可以任何方式复制(即使 something 字段是私有的或受保护的),这是我的示例:

main.cc:

#include <iostream>
class Test {
    public:
        const char* something = "SOMETHING HERE!!!";
        Test(const int& number) : Test(something, number) { }
        // XXX: changed `something` to `_something` to make it different
        Test(const char* _something, const int& number) {
            std::cout << _something << std::endl;
            std::cout << number << std::endl; }
        ~Test() { }
};

int main(int argc, char* argv[]) {
    Test te1(345);
    Test te2("asdasdad", 34523);
    return 0;
}

下面是编译时发生的情况:

g++ main.cc -Os -o main

和运行:

./main

输出是:

pi@pi:~/ $ ./main
A"�~ <-- this is random
345
asdasdad
34523

但是当我使用 -O0-O1-O2 启用优化时......输出只是一个新行:

pi@pi:~/ $ ./main
pi@pi:~/ $

G++ 版本:

pi@pi:~/ $ g++ --version
g++ (Raspbian 6.3.0-18+rpi1) 6.3.0 20170516
const char* something = "SOMETHING HERE!!!";

右侧的默认初始化器,顾名思义,仅在您未在构造函数的初始化器列表中提供显式初始化器时使用。让我们看看你的:

Test(const int& number) : Test(something, number) { }

好的,我们正在委托给另一个构造函数。其他构造函数将执行完全初始化,因此不使用默认初始化程序。但是...我们将 something 的未初始化值作为参数传递。

Test(const char* _something, const int& number) { /* ... */ }

呃哦。现在我们尝试使用 _something 的值,它是 something 的副本,它是不确定的。未定义的行为和火灾随之而来。

你真的不应该将 class 成员的值作为参数传递给它的构造函数,除非你有无限供应的防火鸡和防火蛋。


您正在寻找的行为可以通过将默认值放在对委托构造函数的调用中来获得:

Test(const int& number) : Test("SOMETHING HERE!!!", number) { }

...或将其保存在专用静态变量中:

static constexpr char *const defaultSomething = "SOMETHING HERE!!!";
Test(const int& number) : Test(defaultSomething, number) { }

This is a bug or my fault?

哦,是你的错。默认成员初始化器仅用于在非委托构造函数中初始化成员对象。根据[class.base.init]/9,强调我的:

In a non-delegating constructor, if a given potentially constructed subobject is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then

  • if the entity is a non-static data member that has a default member initializer and either [...] the entity is initialized from its default member initializer as specified in [dcl.init];

所以当您将 something 传递给目标构造函数时,它不会被初始化。您的程序有未定义的行为,并且会崩溃。