有没有人考虑过更严格的" C ++的味道,其中默认情况下需要初始化变量?

时间:2015-02-17 22:47:42

标签: c++ compiler-errors initialization strict

虽然C ++是一种非凡的语言,但它也是一种存在许多陷阱的语言,特别是对于没有经验的程序员而言。我正在谈论类中未初始化的原始类型变量等事情,例如

class Data {
  std::string name;
  unsigned int version;
};

// ...

Data data;
if (data.version) { ... } // use of uninitialized member

我知道这个例子过于简单但实际上即使是经验丰富的开发人员也有时忘记在构造函数中初始化他们的成员变量。虽然默认情况下保留未初始化的原语可能是C的遗留物,但它为我们提供了性能(保留一些数据未初始化)和正确性(初始化所有数据)之间的选择。

好的,但是如果逻辑被倒置怎么办?我的意思是如果所有原语都用零初始化?或者需要显式初始化,否则会产生编译错误。当然,为了获得完全的灵活性,可以使用特殊的语法/类型来保持变量/成员未初始化,例如。

unsigned int x = std::uninitialized_value;

Data::Data() : name(), version(std::uninitialized_value) {}

据我所知,这可能会导致现有C ++代码出现问题,这些代码允许使用未初始化的数据,但新代码可以包含在一个特殊的块中(extern "C"让我想到一个例子)让编译器知道一个特定的部分应严格检查代码的未初始化数据。

抛开兼容性问题,这种方法可以减少代码中的错误,这是我们都感兴趣的。

  1. 你听说过这样的提议吗?
  2. 这样的建议是否有意义?
  3. 你认为这种方法有任何缺点吗?
  4. 注1:我使用了术语" strict"因为这个想法与"严格模式有关"来自Mozilla Developer Network site

    中提到的JavaScript语言
      

    通过将它们更改为throw来消除一些JavaScript无提示错误   错误

    注2:请不要注意提案中使用的建议语法,只是为了说明一点。

    注意3:我意识到像cppcheck这样的工具可以很容易地找到未初始化的成员变量,但我的想法是关于这种检查的编译时支持。

2 个答案:

答案 0 :(得分:1)

  

你有没有听说过这样的提议?
  这样的建议是否有意义?
  你认为这种做法有任何缺点吗?

我没有听说过任何此类提议。为了回答接下来的两个问题,我首先会广泛宣称这两者都没有意义。更具体地说,我们需要将提案分解为两个单独的提案:

  1. 不允许任何未初始化的变量。
  2. “未初始化”变量被秘密初始化为一些明确定义的值(例如,在Java中,使用false0null)。

  3. 案例1有一个简单的反例:表现。例子比比皆是;这是我在代码中使用的(简化)模式:

    Foo x;
    if (/*something*/) {
        x = Foo(...);
    } else {
        x = Foo(...);
    }
    //[do something with x]
    

    x在这里“未初始化”。默认构造函数确实被调用(如果Foo是一个对象),但这可能什么都不做。如果它是一个很大的结构,那么我真正付出的代价就是赋值运算符的代价 - 而不是某些非平凡构造函数的成本加上赋值运算符的代价。如果它是int或类似的typedef,我需要在xor eax,eax之前将其加载到缓存中或清除它。如果if-blocks中有更多代码,那么如果编译器无法忽略它,那么这可能是一个有价值的缓存未命中。


    案例2比较棘手。

    事实证明,现代操作系统实际上在分配给进程时会改变内存的值(这是一个安全问题)。所以,无论如何,这种情况还有一种基本的形式。

    你特别提到such an approach would result in [fewer] bugs in our code。我从根本上不同意。为什么神奇地将事物初始化为一些定义明确的值会使我们的代码更加健壮?实际上,这种半自动初始化会导致巨大的错误数量。

    故事时间!

    完全 如何工作取决于程序的编译方式(所以调试与发布和编译器)。当我第一次学习C ++时,我编写了一个软件光栅化器,并认为它有效 - 因为它在调试模式下完美运行。但是,当我切换到发布模式时,我得到了完全不同的结果。如果操作系统没有在调试模式下将所有内容初始化为零,那么我可能会更早地意识到这一点。这是您建议的引起的错误示例。

    通过一些奇迹,我设法重新找到this令人沮丧的问题,这表明存在类似的混淆。这是一个普遍存在的问题。

    如今,一些调试环境将调试值放入内存。例如。 MSVC将0xDEADBEEF0xFEEEFEEE放在一起。 很多更多here。这与一些OS巫术相结合,允许他们找到未初始化值的使用。在VM中运行代码(例如Valgrind)可以免费获得相同的效果。


    这里更重要的一点是,在我看来,当你忘记初始化它时, 会自动将某些内容初始化为明确定义的值,这与获取某些虚假值一样糟糕(如果不是更糟)。 问题在于程序员在没有理由的情况下期待某些事情 - 他所期望的价值定义明确

答案 1 :(得分:0)

使用-Werror=uninitialized,它完全符合您的要求。

然后,您可以使用unsigned int x = x;

将变量“单元化”