当一个对象有一个父对象时,我们使用组合,它应该关心对象的生命周期。我们在相同的情况下使用unique_ptr
,但对象可以是nullptr
。
当几个外部实体可能需要我们的对象时,我们使用shared_ptr
,因此它的生命周期会延长,直到最后一个外部实体失去兴趣。
在这里,我想问一下另一种生活状况。 如果对象需要持续最短几个持续时间,该怎么办?
这是一个例子。让我们有一个一次性计时器,它存储一个仿函数并在计数完成后执行它。对我来说,这个计时器对象在以下情况下被销毁是有道理的:
1. fulfilling its task - therefore it should be able to destroy istelf
or
2. the parent loosing interest in the timer - so the parent should be able to
destroy the object as well
目前,我使用带有唯一指针的笨拙实现。什么是这个问题的良好模式/指南/实施?
*原因:1)仿函数可能拥有其他一些资源 2)计时器可能已被设置为非常大的数量,然后被放弃 3)如果父项已被销毁,我们通常不希望调用其回调
答案 0 :(得分:2)
这里存在严重的并发和重入问题。
当两个或多个代码有权删除指针时,这两段代码都不能可靠地取消引用该指针,因为这样做时另一个代码可能会破坏它。
同样,任何检查您拥有所有权的分支都可能在任何其他非本地(比如函数调用)运行时变得陈旧,即使没有并发。
我们可以解决这些问题。
template<class T>
struct shared_destroyable {
std::shared_ptr<T> lock() const {
return atomic_load<T>(ptr.get());
}
explicit operator bool() const { return (bool)lock; }
void reset( std::shared_ptr<T> pin = {} ) {
atomic_store(ptr.get(), std::move(pin));
}
shared_destroyable(std::shared_ptr<T> pin):
ptr(std::make_shared<std::shared_ptr<T>>(std::move(pin))
{}
shared_destroyable()=default;
shared_destroyable(shared_destroyable&&)=default;
shared_destroyable(shared_destroyable const&)=default;
shared_destroyable& operator=(shared_destroyable&&)=default;
shared_destroyable& operator=(shared_destroyable const&)=default;
private:
std::shared_ptr<std::shared_ptr<T>> ptr;
};
这种行为模糊不清weak_ptr
shared_ptr
混合。
如果最后一个消失,对象就会被破坏。
但是,如果其中任何一个.reset()
,只要最后一个具有lock()
ed的其他代码已结束其范围,该对象就会被销毁。
引用的作业更改。
所以在使用时你会这样做:
if(auto sp = sd.lock()) {
// use sp
}
并且sp
生命周期保证会持续{}
的范围,即使有人在块中,在另一个线程中执行sd.reset()
,或者在调用其他方法时也是如此
答案 1 :(得分:0)
我认为最好的解决方案是让一个包装器对象具有指向实际计时器对象的唯一指针,以及一个用于计时器对象的getter,如果它已被销毁则返回null。
这样,如果计时器到期并被销毁,那么没有失去兴趣的调用者不会留下一个显然有效但实际上无效的指向计时器对象的指针。