为什么即使在删除对象后我也可以访问成员函数?

时间:2013-08-28 02:00:37

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

我是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 ++中运行的? (即使你删除它之后还有吗?)

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

5 个答案:

答案 0 :(得分:8)

  

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

由于undefined behavior

  

这是如何在C ++中运行的? (即使你删除它之后还有吗?)

因为undefined behavior。无法保证它在其他实现中的工作方式相同。再次,undefined behavior

  

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

delete MC1;
MC1 = nullptr;

通过在nullptr之后将指针设置为delete,运行时最有可能检测到您正在访问无效的,您没有权利的位置。此外,通过努力为所有适用的指针执行此操作,您可以检查对象是否有效(如果非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,这是一个非拥有的,仅观察指针包装器。

答案 1 :(得分:2)

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

答案 2 :(得分:0)

你不走运。记忆还没有写完,事情仍然正常。

有些时候你可能会很幸运,你会很难崩溃,这会突出问题。

访问您delete d的内存不是您应该做的事情。在C ++中,你不应该这样做,因为编译器不会阻止你。

答案 3 :(得分:0)

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->();
    }
};

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

答案 4 :(得分:0)

机制是删除只是告诉系统不再需要内存,可以重新调整用途以便再次使用。没有采取其他行动。这是安全披露中出现的免费使用后错误的基础。程序员可能不再使用内存。

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