封装内置C++类型以保证初始化

Wrapping built-in C++ types to guarantee initialization

本文关键字:初始化 类型 内置 C++ 封装      更新时间:2023-10-16

最近我突然想到,解决"偶尔出现的奇怪错误"的方法往往是简单地初始化我忘记添加到初始化器列表中的类的一个成员变量。

为了防止将来在这些bug上浪费时间,我一直在考虑完全放弃内置的基元类型,并用与基元类型完全相同的包装类替换它们,只是它们总是被初始化。

我精通C++,知道我可以编写非常接近这个目标的类。也就是说,我相信我可以编写一个MyInt类,它的行为非常像一个真正的int。但我对C++也很了解,知道我可能会缺少一两个神秘的东西;)

以前有人做过这样的事吗?有没有指向相关文档的指针,或者需要注意的陷阱列表?这是个好主意吗,还是有我没有看到的缺点?

谢谢!

编辑:感谢大家的评论,这里有最新消息。我开始玩Jarod42的包装器片段,看看是否可以转换一个小爱好项目的代码库。并不完全出乎意料,这是一个相当PITA,但可能是可行的。不过,对于这个问题,它确实开始感觉像是一把巨大的锤子。

编译器警告不是一个真正的选项,因为似乎只有一个警告(-Weffc++)找到了问题,而且它只存在于gcc,即这不是一个安全、可移植的解决方案。

到目前为止,我的结论是,按照Praetorian下面的建议,开始对所有原始成员使用C++11的类初始化。即类似的东西

struct C {
int x = 0;  // yay C++11!
};

希望过一段时间后,提交这样的初始化感觉就像在函数中的代码中声明一个统一的变量一样"赤裸裸"(我很久以前就不这么做了)。这似乎比试图保持初始值设定项列表的最新状态更不容易出错,因为它就在声明中。

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++标志,该标志将警告您未初始化的数据成员(以及其他内容)。

接下来要尝试的是一个静态分析工具来捕捉这些错误。

总之,在对每一种琐碎的数据类型进行装箱之前,我会尝试几件事。

只打开编译器警告要容易得多。大多数优秀的编译器都会警告您不要使用未初始化的变量,因为编译器可能会错过一些边缘情况

尝试更好的调试。

您可以为未初始化的变量打开编译器警告(请参阅此SO问题)。

您还可以使用对代码进行此项检查和其他检查(静态分析)的程序,如列出未初始化变量的cppcheck。

还可以尝试更改编码方式。在C++中,你可以控制何时分配内存,使用什么构造函数,等等。如果你的编码风格是用部分数据构造对象,然后再填充其他部分,那么你很可能会遇到很多未初始化的变量。但是,如果你确保所有构造函数都在有效状态下构造了一个有效的对象,并避免有多个真值点(请参阅单点真值原则),那么你的错误更有可能被编译器捕获——你必须将一个未初始化的变量作为值传递(VC++会警告你),或者在你的构造函数调用中有错误的数字或类型的东西(编译错误),等等

我可以建议你挑选出这类东西的最新来源,以及让你达到目的的结构链,并问你如何更好地重组它吗?C++中有一种特别有纪律的编码风格,它最大限度地利用了编译器,从而尽早提示您。实际上,你在使用这种风格时创建的bug不应该少于多线程问题、资源问题等。

我担心,如果你只是为了防止出现这样的错误而初始化所有东西,你会错过学习那种有纪律的风格,这种风格比未初始化的变量扩展得更远。

以下可能会有所帮助:

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做特殊的工作。

相关文章: