从对象调用的方法可以删除该对象吗?

Kay*_*yla 1 c++

我有点困惑试图理解这里发生的事情。如果一个类对象调用一个方法,然后该方法随后销毁该对象,那么该方法不应该返回,并且应该立即销毁该对象并立即调用析构函数。对吗?但相反,代码只是崩溃了。那么,是否不允许对象调用删除自身的方法?我假设从调用堆栈的工作原理来看,指向对象的指针将被销毁,因此当函数返回到调用者时,在进一步调用调用堆栈时指针不再有效?

class Foo
{
public:
    virtual void Run()
    {
        printf("Entering callback\n");
        RunSomeCallback();
        printf("We're back from callback\n");
    }
    virtual void RunSomeCallback() = 0;
    
    virtual ~Foo()
    {
        printf("We're Destroyed\n");
    }
};

Foo* f = nullptr;

void DestroyFoo();

class Bar : public Foo
{
public:
    virtual void RunSomeCallback()
    {
        printf("Inside callback\n");
        DestroyFoo();
    }
};


void DestroyFoo()
{
    printf("Deleting foo\n");
    delete f;
}
int main()
{
    f = new Bar();
    f->Run();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后,当我使用shared_ptr运行代码时,行为似乎发生了变化,但在这种情况下,析构函数在打印“我们从回调中返回”之前被调用,代码运行,但这是否是未定义的行为?

class Foo
{
public:
    virtual void Run()
    {
        printf("Entering callback\n");
        RunSomeCallback();
        printf("We're back from callback\n");
    }
    virtual void RunSomeCallback() = 0;
    
    virtual ~Foo()
    {
        printf("We're Destroyed\n");
    }
};

std::shared_ptr<Foo> f;

void DestroyFoo();

class Bar : public Foo
{
public:
    virtual void RunSomeCallback()
    {
        printf("Inside callback\n");
        DestroyFoo();
    }
};


void DestroyFoo()
{
    printf("Deleting foo\n");
    f = nullptr;
}
int main()
{
    f = std::make_shared<Bar>();
    f->Run();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

打印顺序

Entering callback
Inside callback
Deleting foo
We're Destroyed
We're back from callback
Run Code Online (Sandbox Code Playgroud)

那么,在对象的所有方法完成之前,该对象是否实际上并未被销毁?

use*_*522 5

当成员函数仍在其上执行时(无论在调用堆栈中的哪个位置),对象被销毁是没有问题的。

但是,在对象的生命周期结束并调用析构函数后delete,任何函数(甚至仍在运行的成员函数)都不能访问该对象的任何成员,并且该对象使用的内存也可以被其他对象重用。(从技术上讲,是否this仍然允许读取也是由实现定义的,但实际上只要this不再取消引用就可以了。)

在您的示例代码中,调用(包括析构函数)后不会访问任何成员delete,因此程序已定义行为并且您给出的输出是预期的。析构函数由 调用delete,结束对象的生命周期,然后Foo::Run继续执行而不再接触该对象。

这两个代码片段都是如此。设置std::shared_ptrtonullptr实际上也只是delete对象。


如果这导致您的系统崩溃,那么我会怀疑编译器错误