我应该重置Destructor中的原始成员变量

Should I reset primitive member variable in destructor?

本文关键字:原始 成员 变量 Destructor 我应该      更新时间:2023-10-16

请参阅以下代码,

class MyClass
{
public: 
       int i;
       MyClass()
       {
              i = 10;
       }
};
MyClass* pObj = nullptr;
int main()
{
       {
              MyClass obj;
              pObj = &obj;
       }
       while (1)
       {
              cout << pObj->i; //pObj is dangling pointer, still no crash.
              Sleep(1000);
       }      
       return 0;
}

OBJ一旦范围出来。但是我在VS 2017中进行了测试,即使使用后,我也没有崩溃。

重置int成员varialbe i

是很好的做法。

在对象被破坏后访问成员是未定义的行为。它可能看起来是一个很好的好方法调试器。

然而,这个想法有缺陷和矮小的系统:

  1. 所有班级都需要进行比赛,而不是专注于创建正确的代码开发人员将花费时间(开发时间和运行时)进行毫无意义的更改。
  2. 编译器发生相当聪明,可以检测到不需要变化。由于正确的程序无法检测到是否进行了更改,因此可能根本无法进行更改。此效果是安全应用程序的实际问题,例如,应该从内存中删除密码,因此无法读取该密码(使用某些不可规定的均值)。
  3. 即使值设置为特定值,内存也将重复使用并且值被覆盖。尤其是在堆栈上的对象中
  4. 即使重置值,您也一定会看到"崩溃":崩溃是由设置的东西引起的,以防止某些事物无效。在您的示例中,您将在堆栈上访问int:从CPU的角度来看,堆栈将保持访问,充其量将获得意外的值。使用不寻常的指针值通常会导致崩溃,因为内存管理系统试图访问未映射但不能保证的位置:在繁忙的32位系统上,几乎所有内存都可能正在使用。也就是说,试图依靠被检测到的未定义行为也是徒劳的。

相应地,最好使用良好的编码实践,避免立即悬挂参考并专注于使用这些习惯。例如,我始终成员初始化列表中初始化成员,即使在极少数情况下,它们最终会在构造函数的正文中变化(即,您'D将您的构造函数写为MyClass(): i() {})。

作为调试工具,它 May 可以合理地替换分配函数(理想情况下是分配器对象,但可能是全局operator new()/operator delete()和带有一个版本,该版本不会很快发出发布的内存和内存和取而代之的是,用可预测的模式填充已发布的内存。由于这些操作减速了程序,您只会在调试构建中使用此代码,但是一次实现一次,并且易于启用/在中央启用/禁用,因此值得付出努力。在实践中,我认为即使这样的系统也没有证明使用托管指针,并且对所有权和终生的适当设计避免了由于参考的大多数错误。

您给出的代码的行为不确定。不确定行为的部分情况正常工作,因此代码有效并不奇怪。代码现在可以正常工作,无论如何它可以根据编译器版本,编译器选项,堆栈内容和月球阶段而破裂。

因此,首先也是最重要的是避免到处悬而未决的指针(以及所有其他不确定的行为)。

如何清理变量在破坏者中,我找到了一个最佳实践:

  1. 遵循编码规则,使我免于访问未分配或破坏对象的错误。我无法用几句话来描述它,但是规则很普遍(请参阅此处和任何地方)。

  2. 分析人类(代码审查)或统计分析仪(例如Cppcheck或pvs-studio或其他)的代码,以避免使用类似于您上面描述的案例。

  3. 请勿手动调用delete,更好地使用scoped_ptr或类似的对象寿命经理。当delete合理时,我通常(通常)将删除后的指针设置为nullptr,以免自己脱离错误。

  4. 使用尽可能罕见的指针。参考是首选。

  5. 当我的班级的对象在外面使用的对象时,我怀疑有人可以在删除后访问它,我可以将签名字段放入内部,将其设置为Destructor中的0xDead之类的东西,然后在Enter或所有公共方法中检查。在这里,请小心不要降低您的代码至无法接受的速度。

从示例到0或-1的所有这些设置i之后,是冗余。至于我,这不是您应该集中注意力的事情。