我有一个给定路径名的函数,查找并返回指向相关值的指针。有时值会存在于静态缓存中,有时会在运行时进行计算和创建。
因此,有时调用者获取所有权并需要在读取后删除该对象,有时则不需要。我想知道,有什么东西我可以用这个指针包装,以便调用者可以根据需要自动释放它吗?
我原以为我可能能够使用unique_ptr,但不是该类型的删除部分,所以我怎么能返回有时会执行但有时不会删除的相同类型。
实际上,一个解决方案可能是为函数内部创建的值返回正常的std::shared_ptr
,另一个解决方案为映射中的值返回一个空删除器。
您可以看到两个用例如何不需要调用代码中的任何操作,并且完全透明。
你可以使用std::unique_ptr
和一个知道是否要释放的删除器。虽然deleter类型是unique_ptr
类型的一部分,但是不同的unique_ptr实例可以具有不同的删除器实例:
template <class T>
class delete_if_not_cached {
bool cached;
public:
delete_if_not_cached(bool c = false) : cached(c) {}
void operator()(T *obj) { if (!cached) delete obj; }
}
你有你的功能返回一个std::unique_ptr<T, delete_if_not_cached<T>>
。如果要将指针返回到缓存中,则将该指针创建为:
return std::unique_ptr<T, delete_if_not_cached<T>>(raw_pointer, delete_if_not_cached<T>(true));
要返回非缓存对象,请使用
return std::unique_ptr<T, delete_if_not_cached<T>>(new T(...))
一个潜在的缺陷是,如果你从缓存中删除东西,可能会留下你之前返回的悬挂unique_ptr
s。如果这是一个问题,使用shared_ptr
s返回和缓存本身可能更有意义。
您可以使用std::shared_ptr
,但这并不能真正描述您的所有权模型。您是否考虑过滚动自己的包含std::unique_ptr
和原始指针的包装器并根据具体情况使用正确的包装器?就像是:
#include <cassert>
#include <memory>
class MyClass { };
class Wrapper {
const MyClass* cached_;
std::unique_ptr<MyClass> owned_;
public:
Wrapper() : cached_(nullptr) {}
void setCached(const MyClass* cached) {cached_ = cached;}
void setOwned(std::unique_ptr<MyClass> owned) { owned_ = std::move(owned); }
const MyClass* get() const {return cached_ ? cached_ : owned_.get();}
};
Wrapper getWrapper(int i) {
static MyClass first;
static MyClass second;
Wrapper wrapper;
if (i == 0)
wrapper.setCached(&first);
else if (i == 1)
wrapper.setCached(&second);
else
wrapper.setOwned(std::unique_ptr<MyClass>(new MyClass()));
return wrapper;
}
int main() {
for (int i = 0; i != 4; ++i) {
Wrapper wrapper = getWrapper(i);
assert(wrapper.get() != nullptr);
}
}
包装器可以将调用转发给真实类,也可以提供对真实类的原始指针的访问。
或者包装器可以多态地工作,具有接口和两个实现。一个带有原始指针,另一个带有唯一指针:
#include <cassert>
#include <memory>
class MyClass {};
class Wrapper {
public:
virtual ~Wrapper() = 0;
virtual const MyClass* get() const = 0;
};
Wrapper::~Wrapper() {}
class OwnerWrapper : public Wrapper {
std::unique_ptr<MyClass> owned_;
public:
OwnerWrapper(std::unique_ptr<MyClass> in) : owned_(std::move(in)) {}
virtual const MyClass* get() const { return owned_.get(); }
};
class PtrWrapper : public Wrapper {
const MyClass* ptr_;
public:
PtrWrapper(const MyClass* ptr) : ptr_(ptr) {}
virtual const MyClass* get() const { return ptr_; }
};
std::unique_ptr<Wrapper> getWrapper(int i) {
static MyClass first;
static MyClass second;
if (i == 0)
return std::unique_ptr<Wrapper>(new PtrWrapper(&first));
else if (i == 1)
return std::unique_ptr<Wrapper>(new PtrWrapper(&second));
else {
std::unique_ptr<MyClass> myclass(new MyClass());
return std::unique_ptr<Wrapper>(new OwnerWrapper(std::move(myclass)));
}
}
int main() {
for (int i = 0; i != 4; ++i) {
auto wrapper = getWrapper(i);
assert(wrapper->get() != nullptr);
}
}