假设我们有以下课程:
Class MyClass
{
void func();
std::shared_ptr<std::function<void()>> getFuncPointer(); // returns pointer to func()
};
假设有一个其他类的对象(我们称它为
objectB
),它拥有一个shared_ptr
到一个objectA
类型的对象(MyClass
)。假设 objectB
是 objectA
的唯一所有者/用户,并且没有其他人可以访问 objectA
.
以下顺序会发生什么:
objectB
调用 objectA->getFuncPointer()
并存储返回的 shared_ptr
.objectB
来电objectA.reset()
问题:
objectA
的析构函数被调用了吗?
如果
objectB
没有 shared_ptr
objectA
的方法之一,答案显然是肯定的。
我不确定的是
objectB
获得shared_ptr
对objectA
的方法之一的影响。
如果答案是YES:
如果
objectA
被破坏,但另一个线程通过函数指针仍在 func()
中,那么如果我们访问成员变量会发生什么?似乎有问题,因为objectA
与其成员一起消失了。
如果答案是NO:
似乎有问题,因为我看不到成员函数的
shared_ptr
如何可能使整个对象保持活动状态。函数指针对包含的对象一无所知,对吧?
这两种情况似乎都有问题,所以这就是为什么我不确定哪个是正确答案。
成员函数/方法通常在编译器中实现为全局函数,并带有一个额外的第一个参数来接收对象的实例,因此
std::function <void()>
的类型在引用方法函数时实际上是 void (MyClass&)
/void (MyClass*)
MyClass
[这就是为什么当您尝试为成员函数创建 std::bind
时,您也将对象实例作为第一个参数传入]。
通过返回
shared_ptr
的 std::function<void()>
,您基本上是在为 &MyClass::func() 构造一个包含 shared_ptr
的 std::function<void(this)>
,它对实例没有实际所有权,所以答案是 YES, shared_ptr
不应阻止对象的破坏。
编辑: 刚看到你的其他评论,正如我所说的,这些方法就像其他自由函数一样是函数,它们位于进程内存的 CODE 部分,它们不占用 DATA 内存,因此它们与对象的生命周期无关,无论您创建 1 个
MyClass
对象还是 10000 个 MyClass
对象,它们的单个副本(在每个翻译单元的正常情况下)都会存在,因此它们在程序的整个运行时都是可调用的
答案是肯定的:一个对象可以而且将会被删除,即使有一个
std::shared_ptr
间接依赖它。通过为给定对象(在本例中为函数)创建 std::shared_ptr
,只要该对象具有共享所有者,您就可以保留该对象,因此您必须保证该对象在该时间内有效。如果你违背诺言,事情就会出错。
这与拥有一个指向对象O2的对象O1并没有什么不同,然后
delete
-ing O2而O1还在附近。即使 O1 本身通过 std::shared_ptr
持有,也不会保留 O2.
这两种情况似乎都有问题,所以这就是为什么我不确定哪个是正确答案。
与许多语言不同,C++ 不尝试“安全”。编写调用未定义行为的代码非常容易;使用 C++ 工作的一个重要部分是避免这样做。
std::shared_ptr
是help 你写safe 代码的工具,但它不会stop 你写unsafe 代码。
很可能是的,它被删除了。对象 A 有一种可能的方法可以通过
getFuncPointer
到 B 来延长它自己的生命周期。那就是如果 getFuncPointer 返回的 std::function 正在捕获实例本身。
使用 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]() {
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)();
return 0;
上面的
a.reset()
调用会将 shared_ptr 的引用计数减 1,但在 spFunc 中仍然捕获了另一个未完成的引用。当a
超出范围并释放其捕获的变量时,对象spFunc
最终将被删除。