【问题标题】:Why I can access member functions even after the object was deleted?为什么即使在对象被删除后我也可以访问成员函数?
【发布时间】:2013-08-30 22:59:04
【问题描述】:

我是 C++ 新手,从我目前所学到的知识来看,当您对指向在堆上创建的内容的指针调用 delete 时,该指针指向的任何内容都会被擦除并释放内存,对吗?

但是当我在一个简单的类上尝试这个时:

class MyClass
{
    int _Id;
public:
    MyClass(int id) : _Id(id)
    {
        std::cout << "$Constructing the damn thing! " << _Id << std::endl;
    }
    ~MyClass()
    {
        std::cout << "?Destructing the damn thing! " << _Id << std::endl;
    }
    void Go_XXX_Your_Self()
    {
        std::cout << "%OooooooooO NOOOOOO! " << _Id << std::endl;
        delete this;
    }
    void Identify_Your_Self()
    {
        std::cout << "#Object number: " << _Id << " Located at: " << this << std::endl;
    }
};

这些只是一些愚蠢的测试,看看删除是如何工作的:

int main()
{
    MyClass* MC1 = new MyClass(100);
    MyClass* MC2 = new MyClass(200);
    MyClass* MC3 = MC2;

    std::cout << MC1 << " " << MC2 << " " << MC3 << " " << std::endl;

    MC1->Identify_Your_Self();
    MC2->Identify_Your_Self();
    MC3->Identify_Your_Self();

    delete MC1;

    MC1->Identify_Your_Self();


    MC3->Go_XXX_Your_Self();

    MC3->Identify_Your_Self();


    delete MC2;

    MC2->Identify_Your_Self();

    MC2->Go_XXX_Your_Self();

    MC2->Identify_Your_Self();

    return 0;
}

这是输出:

$Constructing the damn thing! 100
$Constructing the damn thing! 200
0x3e3e90 0x3e3eb0 0x3e3eb0
#Object number: 100 Located at: 0x3e3e90
#Object number: 200 Located at: 0x3e3eb0
#Object number: 200 Located at: 0x3e3eb0
?Destructing the damn thing! 100
#Object number: 0 Located at: 0x3e3e90
%OooooooooO NOOOOOO! 200
?Destructing the damn thing! 200
#Object number: 4079248 Located at: 0x3e3eb0
?Destructing the damn thing! 4079248
#Object number: 4079280 Located at: 0x3e3eb0
%OooooooooO NOOOOOO! 4079280
?Destructing the damn thing! 4079280
#Object number: 4079280 Located at: 0x3e3eb0

所以,我的问题是,为什么即使在对象被删除后我仍然能够调用 Go_XXX_Your_Self() 和 identify_Your_Self()?

这就是它在 C++ 中的工作方式吗? (删除后还有吗?)

您也可以检查一下它是否不存在吗? (我知道理论上是不可能的,但我很想知道有什么方法)

【问题讨论】:

  • 你只是在与操作系统赛跑并赢了……你最终会输。
  • 请注意0x3e3eb0 号码处的对象如何从200 更改为4079280
  • 因为未定义的行为是个没心没肺的丫头。迟早,她会打破你的。
  • 未定义行为!!!你的应用迟早会崩溃.. :)
  • 不是完全重复,但它解释了您想知道的内容:stackoverflow.com/questions/6441218/…

标签: c++ function object member delete-operator


【解决方案1】:

所以,我的问题是,为什么即使在对象被删除后我仍然能够调用 Go_XXX_Your_Self() 和 identify_Your_Self()?

因为undefined behavior

这就是它在 C++ 中的工作方式吗? (删除后还有吗?)

因为undefined behavior。不能保证它在其他实现上也能正常工作。再次,undefined behavior

您也可以检查一下它是否不存在吗? (我知道理论上是不可能的,但我很想知道有什么方法)

delete MC1;
MC1 = nullptr;

通过在delete 之后将指针设置为nullptr,运行时最有可能检测到您正在访问一个无效的、您没有使用权的位置。此外,通过对所有适用的指针认真执行此操作,您可以检查对象是否有效(如果非nullptr 则有效)。

if(my_ptr) {
   // my_ptr is most possibly valid (though you can still go wrong)
   // use my_ptr
}

同样,您还应该将原始指针设置为nullptr,当它们尚未初始化为某个有效地址时。

MyClass* some_ptr = nullptr;
...

但同样,如果您可以使用现代 C++11 工具,最好不要使用原始指针,而只使用 std::unique_ptrstd::shared_ptr(取决于您所需的语义)。在未来的 C++ 标准修订版中,您可能还想使用 proposed std::exempt_ptr,它是一个非拥有的、仅观察的指针包装器。

【讨论】:

    【解决方案2】:

    当您在一个对象上调用delete 时,该对象使用的内存可供new 的另一个使用(或者,实际上,任何使用堆的东西)使用。在此之前,已删除的对象可能(或可能)保留其先前的值。但最终,随着您的程序继续运行,被删除对象所使用的内存 将被覆盖,如果幸运的话,将会发生坏事:您的程序会在您身上崩溃。如果你不走运,你的程序在部署到现场之前不会崩溃。

    【讨论】:

    • +1。请注意,您的头像与一位非常活跃的 C++ 社区成员在此处使用的头像非常相似。
    • @Potatoswatter: 好吧,我猜巧合会发生......我几年前创建了这个头像,而且大多不太关心它,但似乎许多论坛会自动从 gravatar.org 检索它.
    【解决方案3】:

    你运气不好。内存还没有写完,一切照旧。

    其他时候你可能会很幸运,你会严重崩溃,这会突出问题。

    访问您拥有deleted 的内存不是您应该做的事情。在 C++ 中,您可以不这样做,因为编译器不会阻止您。

    【讨论】:

      【解决方案4】:

      C++ 编译器通常不会生成代码来代表您检查指针的有效性。如果您关心,您需要自己提供代码。由于访问已释放的内存会调用未定义的行为,因此这样做的结果是不确定的。

      无论如何,您都不应该操纵原始指针,而应为您依赖其中一个智能指针。然后,您遇到此错误的可能性就会大大降低,因为您将依赖指针超出范围来破坏内存。

      不过,即便如此,您仍然可能会遇到强迫坏事发生的情况,例如:

          std::unique_ptr<Foo> p(new Foo);
          p->foo();
          p.reset(0);
          p->foo();
      

      您可以尝试使用智能指针包装器在运行时捕获此类问题,该包装器会为您进行某种形式的检查:

      template <typename T>
      class CheckedSharedPtr {
          std::shared_ptr<T> ptr_;
      public:
          //...
          T * operator -> () const {
              if (ptr_ == 0) {
                  //...throw something
              }
              return ptr_.operator->();
          }
      };
      

      但是,这只会给每个指针访问增加额外的开销。更好的方法是使用一个好的静态代码分析工具,它可以帮助识别代码中的这类问题以及许多其他问题。

      【讨论】:

        【解决方案5】:

        删除的机制是简单地告诉系统内存不再需要并且可以重新用于再次使用。没有采取其他措施。这是安全披露中出现的释放后使用错误的基础。程序员是否再次使用内存取决于程序员。

        为此,RAII 是尝试减少此问题事件的一种方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-04-29
          • 2016-04-30
          • 1970-01-01
          • 1970-01-01
          • 2015-07-02
          • 1970-01-01
          • 1970-01-01
          • 2013-06-17
          相关资源
          最近更新 更多