是否有一种惯用的方法来返回一个可选地拥有其值的指针

时间:2014-04-22 08:05:04

标签: c++ stl

我有一个给出路径名的函数,查找并返回指向相关值的指针。有时值会存在于静态缓存中,有时会在运行中进行计算和创建。

因此,有时调用者获取所有权并需要在读取后删除该对象,有时则不需要。我想知道,有什么东西可以包装这个指针,以便调用者可以根据需要自动释放它吗?

我原以为我可以使用unique_ptr,但不是该类型的删除部分,所以我怎样才能返回有时会执行但实际上不会删除的相同类型。

3 个答案:

答案 0 :(得分:4)

实际上,一个解决方案可能是为函数内部创建的值返回正常std::shared_ptr,另一个解决方案为映射中的值返回一个空删除函数。

Live example of this solution

您可以看到这两个用例如何不需要调用代码中的任何操作,并且完全透明。

答案 1 :(得分:1)

您可以使用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);
  }
}

答案 2 :(得分:1)

您可以将std::unique_ptr与知道是否免费的删除者一起使用。删除器类型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。如果这是一个问题,那么使用shared_ptr返回和缓存本身可能更有意义。