繁体   English   中英

为什么我没有收到有关在 ctor 中访问未初始化成员变量的编译器警告?

[英]Why do I not get compiler warning about access uninitialized member variable in ctor?

这是一个简单的测试用例,编译时没有任何警告。 看起来是一个常见的错误,但在这种情况下,clang、gcc 和 Visual Studio 不会发出警告。 为什么?

class Image {
  private:
    int width, height;
    int* array;
  public:
    Image(int _width, int _height);
    void crashTest();
};
Image::Image(int _width, int _height)
{
  array = new int[width * height];
               // ^^^^^   ^^^^^^ this is wrong
               // I expect a warning here e.g.: 'width is uninitialized here'
  width = _width;
  height = _height;
}
void Image::crashTest()
{
  for (int x = 0; x < width; ++x)
  {
    for (int y = 0; y < height; ++y)
      array[x + y * width] = 0;
  }
}
int main()
{
  const int ARRAY_SIZE = 1000;
  Image image(ARRAY_SIZE, ARRAY_SIZE);
  image.crashTest();
  return 0;
}

例如:

g++ -Wall -Wextra -O2 -Wuninitialized test.cpp
clang++ -Wall -Wextra -O2 -Wuninitialized test.cpp

不给我任何警告

这个老问题的后续:通过使用-Weffc++启用来自“Effective C++”的警告,您可以使用 g++ 获得您正在寻找的警告。 这将抱怨未显式初始化的成员变量。

这可能过于激进,因为它还会抱怨具有未显式初始化的默认构造函数的类成员。

我还没有看到 Clang 的等效选项——我同意有关具有未初始化原始数据成员的类的警告将非常有用。

简答

正如评论中所指出的,从未初始化的变量中读取是未定义的行为。 编译器没有义务为此提供警告。

(事实上​​,只要你的程序表达了未定义的行为,编译器就可以有效地免除任何和所有义务......)

来自标准的[defns.undefined]部分(添加了重点):

未定义的行为

本国际标准没有要求的行为

[注意:当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。 允许的未定义行为范围从完全忽略情况并产生不可预测的结果,在翻译或程序执行期间以环境特征的文件化方式(有或没有发布诊断消息),到终止翻译或执行(通过发布诊断消息)。 许多错误的程序结构不会产生未定义的行为; 他们需要被诊断。 ——尾注]


长答案

对于编译器来说,这可能是一个很难检测到的情况(如果它确实检测到它,则很难以某种有用的方式通知用户)。

您的代码仅表现出未定义的行为,因为它试图从未初始化的成员变量widthheight读取。 它们是成员变量的事实是使这种情况难以诊断的原因之一。

对于局部变量,检测到的静态分析可能相对简单(注意,并非总是如此)。

例如,很容易在这里看到问题:

    int foo()
    {
        int a;
        int b = 0;

        return a + b; // Danger! `a` hasn't been initialized!
    }

在这种情况下如何:

    int foo(int& a)
    {
        int b = 1;

        return a + b; // Hmm... I sure hope whoever gave me `a` remembered to initialize it first 
    }

    void bar()
    {
        int value;
        int result = foo(value); // Hmm... not sure if it matters that value hasn't been initialized yet
    }

一旦我们开始处理范围超出单个块的变量,就很难检测变量是否已初始化。

现在,将其与手头的问题(您的问题)联系起来:变量widthheight不是构造函数的本地变量 -它们可能已在构造函数之外初始化

例如:

    Image::Image(int _width, int _height)
    {
      Initialize();

      array = new int[width * height]; // Maybe these were initialized in `Initialize`...

      width = _width;
      height = _height;
    }

    Image::Initialize()
    {
        width = 0;
        height = 0;
    }

在这种情况下,编译器是否应该发出警告?

经过一些粗略的分析,我们可以得出结论说“不,它不应该警告”,因为我们可以看到Initialize方法确实初始化了有问题的成员变量。

但是如果Initialize将此委托给另一个方法MoreInitialize()呢? 该方法将其委托给另一个方法YetEvenMoreInitialize 这开始看起来像是一个我们无法合理期望编译器解决的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM