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

时间:2014-05-29 15:57:41

标签: c++ constructor initialization compiler-warnings

这是一个简单的测试用例,无需任何警告即可编译。看起来像一个常见的错误,但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;
}

e.g:

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

没有给我任何警告

2 个答案:

答案 0 :(得分:5)

简答

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

(事实上,只要您的程序表达未定义的行为,编译器就会从任何和所有义务中有效释放......)

从标准的[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方法确实初始化了有问题的成员变量。 / p>

但是如果Initialize将此代理委托给另一个方法MoreInitialize()怎么办?那个方法将它委托给另一个方法YetEvenMoreInitializeThis begins to look like a problem我们无法合理地期望编译器能够解决。

答案 1 :(得分:4)

这个老问题的后续问题:你可以通过启用来自&#34; Effective C ++&#34;的警告来获得你正在寻找g ++的警告。与-Weffc++。这将抱怨未明确初始化的成员变量。

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

我没有看到Clang的等效选项 - 我同意对未初始化的原始数据成员的类进行警告非常有用。