如果一个C++对象的成员函数有一个未完成的shared_ptr,那么它的析构函数会被调用吗?

p1tboqfb  于 2023-03-20  发布在  其他
关注(0)|答案(3)|浏览(189)

假设我们有以下类:

Class MyClass
{
    void func();
    std::shared_ptr<std::function<void()>> getFuncPointer(); // returns pointer to func()
};

假设有一个其他类的对象(我们称之为objectB)拥有MyClass类型的对象(objectA)的shared_ptr,假设objectBobjectA的唯一所有者/用户,其他人都没有访问objectA的权限。
按以下顺序发生的情况:

  1. objectB调用objectA->getFuncPointer()并存储返回的shared_ptr
  2. objectB呼叫objectA.reset()
    问:objectA的析构函数被调用了吗?
    如果objectB * 没有 * shared_ptrobjectA的方法之一,答案显然是肯定的。
    我不确定的是objectB获取一个shared_ptrobjectA的一个 * 方法 * 的影响。
    如果回答为
    如果objectA被析构,但是另一个线程通过函数指针仍然在func()中,那么如果我们访问一个成员变量会发生什么?看起来有问题,因为objectA和它的成员沿着消失了。
    如果回答为
    看起来很有问题,因为我不明白一个成员函数的shared_ptr如何可能保持整个对象的活动。函数指针对包含的对象一无所知,对吗?
    这两种情况似乎都有问题,所以这就是为什么我不知道哪一个是正确的答案。
qltillow

qltillow1#

答案是肯定的:即使有std::shared_ptr间接地依赖于一个对象,它也可以被删除,通过创建一个std::shared_ptr给一个给定的对象(这里是一个函数),只要它有一个共享的所有者,你就可以保留这个对象,所以你必须保证这个对象在这个时间内是有效的,如果你违背了这个承诺,事情就会出错。
这和有一个对象 O1,它持有一个指向对象 O2的指针,然后在 O1还在的时候delete-ing O2没有什么区别,即使 *O *1本身是通过std::shared_ptr持有的,也不能保留 O2。
这两种情况似乎都有问题,所以这就是为什么我不知道哪一个是正确的答案。
与许多语言不同,C并不试图保证“安全”,编写调用 * undefined behavior * 的代码非常容易;在C中工作的一个重要部分就是避免这样做。std::shared_ptr是一个“帮助”您编写“安全”代码的工具,但它不会“阻止”您编写“不安全”代码。

hgc7kmma

hgc7kmma2#

最有可能的是,它会被删除,根据上面的@ruakh给予的细节。
但是对象A可以通过对象B调用的getFuncPointer或其他方法来延长自己的生命周期,那就是getFuncPointer返回的std::函数是否捕获了示例本身。
考虑使用enable_shared_from_this实现此实现

class MyClass : public std::enable_shared_from_this<MyClass>
{
public:
    void func();

    std::shared_ptr<std::function<void()>> getFuncPointer()
    {
        auto spThis = shared_from_this(); // get a shared_ptr to "this"

        auto fn = [spThis]() {   // capture spThis by value increases yet another ref on the shared_ptr
            spThis->func();
        };

        auto result = std::make_shared<std::function<void()>>();
        *result = fn;
        return result;
    }
};

如果你有这个实现

auto a = std::make_shared<MyClass>();
    auto spFunc = a->getFuncPointer();
    a.reset();
    (*spFunc)();

上面的a.reset()调用将shared_ptr的引用计数减少1,但spFunc中仍有另一个未完成的引用。当spFunc超出作用域并释放其捕获的变量时,对象a最终将被删除。

yquaqz18

yquaqz183#

成员函数/方法通常在编译器中实现为全局函数,带有额外的第一个参数接收对象的示例,因此当引用MyClass的方法函数时,std::function <void()>的类型实际上将是void (MyClass&)/void (MyClass*) [这就是为什么当您试图为成员函数创建std::bind时,也将对象示例作为第一个参数传入]。
通过返回std::function<void()>shared_ptr,您基本上是在为&MyClass::func()构造一个包含std::function<void(this)>shared_ptr,它不拥有示例的实际所有权,所以答案是YESshared_ptr不应该阻止对象的销毁。
编辑:我刚刚看到你的另一条评论,正如我所说的,这些方法和其他自由函数一样是函数,它们驻留在进程内存的CODE部分,它们不占用DATA内存,因此它们与对象的寿命无关,它们的一份拷贝(在每个翻译单元的正常情况下)将出现在那里,无论您创建1个MyClass对象还是10000个MyClass对象,因此它们在程序的整个运行时都是可调用的

相关问题