最近我想到,我经常承认,修复“偶尔会出现的奇怪错误”只是简单地初始化我忘记添加到初始化列表的类的一个成员变量
为了防止将来浪费时间在这些错误上,我一直在努力完全抛弃内置的原始类型,并用与其原始对应物完全相同的包装类替换它们,除了它们总是如此被初始化。
我非常精通C ++,知道我可以编写非常接近该目标的类。即我有信心我可以编写一个非常像真正的int的MyInt类。但我也非常了解C ++,知道可能有一两件我不知道的神秘事物;)
以前有人做过这样的事吗?任何指向相关文档的指针,或者需要注意的陷阱列表?这是一个好主意,还是有任何缺点我还没有看到?
谢谢!
编辑:感谢大家的评论,这是一个更新。我开始玩Jarod42的包装片段,看看我是否可以转换一个小的爱好项目代码库。不完全出乎意料的是,这是一个非常PITA,但可能是可行的。尽管如此,它确实开始感觉像是一把非常大的锤子。
编译器警告不是一个真正的选择,因为只有一个(-Weffc ++)似乎找到了问题,它只存在于gcc,即这不是一个安全的,可移植的解决方案。
到目前为止,我的结论是开始使用C ++ 11的所有原始成员的类内初始化,如下面的Praetorian所建议的那样。即
之类的东西struct C {
int x = 0; // yay C++11!
};
..并且希望在一段时间之后,省略这样的初始化感觉为“裸”,因为在函数内的代码中声明了一个单元化变量(我很久以前就已经停止了)。这似乎比尝试保持初始化列表最新更容易出错,因为它与声明一致。
答案 0 :(得分:2)
C ++ 11允许通过允许非静态数据成员的类内初始化来避免这种陷阱。例如:
struct foo
{
foo(int i) : i(i) {} // here i will be set to argument value
foo() {} // here i will be zero
int i = {}; // value (zero) initialize i
};
此功能也不仅限于普通类型。因此,只要在类定义中声明数据成员,就开始初始化数据成员。
如果您的编译器不支持此功能,请尝试启动警告级别以查看它是否会告诉您未初始化的数据成员。例如,g ++有-Weffc++
标志,它会警告你未初始化的数据成员(以及其他内容)。
接下来要尝试的是一个静态分析工具来捕捉这些错误。
总而言之,在沿着装箱每一个简单数据类型的路径前,我会尝试一些事情。
答案 1 :(得分:1)
打开编译器警告要容易得多。大多数体面的编译器会警告你使用未初始化的变量 - 授予编译器可能遗漏的一些边缘情况。
答案 2 :(得分:1)
尝试更好的调试。
您可以为未初始化的变量启用编译器警告(请参阅this SO question)。
您还可以使用执行此操作的程序以及其他代码检查(静态分析),例如cppcheck,其中列出了未初始化的变量。
还可以尝试更改编码方式。在C ++中,您可以控制何时分配内存,使用哪些构造函数等。如果您使用以部分数据构造对象的样式进行编码,然后再填充其他部分,那么您很可能会遇到未初始化的变量。但是如果你确保所有构造函数都构造一个有效状态的有效对象,并避免有多个真实点(参见单点真相原则),那么你的错误很可能被编译器捕获 - 你会有将未初始化的变量作为值传递(VC ++将警告您),或者在构造函数调用中编写错误的数字或类型(编译错误)等。
我可能会建议你挑选出最新的这种东西来源,完整的结构链让你在那里,并问你如何更好地重组它? C ++中有一种特殊规范的编码风格,可以最大限度地利用编译器,从而尽早提示您。您使用该样式时创建的错误实际上不应该是多线程问题,资源问题等。
我担心,如果你为防止出现这些错误而对所有内容进行初始化,那么你就会错过学习那种比未初始化变量更进一步延伸的规范风格。
答案 3 :(得分:1)
以下可能会有所帮助:
template <typename T>
class My
{
public:
constexpr My() : t() {}
constexpr My(const T&t) : t(t) {}
operator T& () { return t; }
constexpr operator const T& () const { return t; }
const T* operator& () const { return &t; }
T* operator& () { return &t; }
private:
T t;
};
请注意,确定是否最好检查是否使用My<int>
代替每个可能未初始化的int
...
但请注意,无论如何你必须为union
做特殊工作。