【问题标题】:Forbid copy-construction of base class禁止复制构造基类
【发布时间】:2014-11-18 16:25:59
【问题描述】:

我想快速实现一些所谓的“所有者指针”,即确保唯一所有权语义的智能指针,同时提供不让对象保持活动状态但可以测试它是否存在的“观察者”指针是。

我尝试做的最直接的方法是继承std::shared_ptr,并禁用它的复制构造,以便没有其他指针可以实际共享该对象。

这是我现在拥有的:

#include <memory>
#include <iostream>

template <class T>
struct owner_ptr : public std::shared_ptr<T> {

    // Import constructors
    using std::shared_ptr<T>::shared_ptr;

    // Disable copy-construction
    owner_ptr(owner_ptr<T> const&) = delete;

    // Failed attempt at forbidding what comes next
    operator std::shared_ptr<T> const&() = delete;
};

struct Foo {
    Foo() {
        std::cout << "Hello Foo\n";
    }

    ~Foo() {
        std::cout << "G'bye Foo\n";
    }

    void talk() {
        std::cout << "I'm talkin'\n";
    }
};

owner_ptr<Foo> fooPtr(new Foo);

int main(int, char**) {

    // This should not compile, but it does.
    std::shared_ptr<Foo> sptr = fooPtr;

    // Simple tests
    fooPtr->talk();
    (*fooPtr).talk();

    // Confirmation that two pointers are sharing the object (it prints "2").
    std::cout << sptr.use_count() << '\n';
}

我一直在拉头发。如何禁止从我的 owner_ptr 复制构造 std::shared_ptr ?我不喜欢私下继承然后从std::shared_ptr导入所有东西...

【问题讨论】:

  • 你真的想在这里使用组合而不是继承。尤其不是公共基地。这就是你所有麻烦的根源。
  • @Deduplicator 我会将所有内容转发给所述成员,这与我在上一句中所说的完全相同。如果没有别的办法,我会作为最后的手段去做,但我不喜欢它。
  • 那么,你想创建类似std::unique_ptr的东西吗?
  • @BЈовић std::unique_ptr 不提供观察者指针,这是我需要的主要功能。
  • 究竟什么是“观察者指针”?

标签: c++11 copy-constructor smart-pointers base-class


【解决方案1】:

我不认为继承std::shared_ptr 是要走的路。如果您真的想正确地做到这一点,我认为您应该自己实现它,包括所有引用计数。实现智能指针实际上并不那么困难。

但是,在大多数情况下,如果您只是想要满足您需求的东西,请使用组合。

我很好奇你想做什么,我不相信这是一个好主意,但我尝试使用组合实现 OwnerPointerObserverPointer 对:

#include <memory>
#include <iostream>

struct Foo {
  Foo() {std::cout << "Hello Foo\n"; }
  ~Foo() { std::cout << "G'bye Foo\n"; }
  void talk() { std::cout << "I'm talkin'\n"; }
};

template <class T>
class ObserverPointer;  // Forward declaration.

template<class T>
class OwnerPointer;  // Forward declaration.

// RAII object that can be obtained from ObserverPointer
// that ensures the ObserverPointer does not expire.
// Only operation is to test validity.
template <class T>
class ObserverLock {
 friend ObserverPointer<T>;
 private:
  std::shared_ptr<T> impl_;
  ObserverLock(const std::weak_ptr<T>& in) : impl_(in.lock()) {}
 public:
  // Movable.
  ObserverLock(ObserverLock&&) = default;
  ObserverLock& operator=(ObserverLock&&) = default;

  // Not copyable.
  ObserverLock& operator=(const ObserverLock&) = delete;
  ObserverLock(const ObserverLock&) = delete;

  // Test validity.
  explicit operator bool() const noexcept { return impl_ != nullptr;} 
};

template <class T>
class ObserverPointer {
 private:
  std::weak_ptr<T> impl_;
  T*               raw_;
 public:
  ObserverPointer(const OwnerPointer<T>& own) noexcept : impl_(own.impl_), raw_(own.get()) {}

  T* get() const { return raw_; }
  T* operator->() const { return raw_; }
  T& operator*() const { return *raw_; }

  ObserverPointer() : impl_(), raw_(nullptr) { }
  ObserverPointer(const ObserverPointer& in) = default;
  ObserverPointer(ObserverPointer&& in) = default;
  ObserverPointer& operator=(const ObserverPointer& in) = default;
  ObserverPointer& operator=(ObserverPointer&& in) = default; 

  bool expired() { return impl_.expired(); }  
  ObserverLock<T> lock() { return ObserverLock<T>(impl_); }
};

template <class T>
struct OwnerPointer {    
 friend ObserverPointer<T>;    
 private:
  std::shared_ptr<T> impl_;
 public:

  // Constructors
  explicit OwnerPointer(T* in) : impl_(in) {}
  template<class Deleter>
  OwnerPointer(std::unique_ptr<T, Deleter>&& in) : impl_(std::move(in)) { }
  OwnerPointer(std::shared_ptr<T>&& in) noexcept : impl_(std::move(in)) { }
  OwnerPointer(OwnerPointer<T>&&) noexcept = default;  
  OwnerPointer(OwnerPointer<T> const&) = delete;

  // Assignment operators
  OwnerPointer& operator=(OwnerPointer<T> const&) = delete;
  OwnerPointer& operator=(OwnerPointer<T>&&) = default;

  T* get() const { return impl_.get(); }
  T* operator->() const { return impl_.get(); }
  T& operator*() const { return *impl_; }

  explicit operator ObserverPointer<T>() const noexcept { return ObserverPointer<T>(impl_);}
  explicit operator bool() const noexcept { return impl_;}
};

// Convenience function equivalent to make_shared
template <class T, class... Args>
OwnerPointer<T> make_owner(Args && ...args) {
  return OwnerPointer<T>(new T(std::forward<Args>(args)...));
}

int main() {
  auto owner = make_owner<Foo>();
  ObserverPointer<Foo> observer = owner; 
  auto lock = observer.lock();
  if (lock)
   observer->talk();
}

Live demo.

它可能需要一些工作,并且它不提供 std::shared_ptr 和 std::weak_ptr 的完整功能集,但在大多数情况下它不需要,只需创建您需要的。

我通过提供一个 RAII ObserverLock 对象来扩展“唯一所有权”的定义,该对象只能用于保持 ObserverPointer 活着。从技术上讲,它“拥有”指针,但它的功能非常有限,您不能创建多个“OwnerPointer”。

【讨论】:

  • 虽然它没有回答我的问题,但它确实解决了我的问题。我正要卷起袖子自己实现它,但我不会像你设计的那样想到ObserverLock。谢谢!
猜你喜欢
  • 2012-05-15
  • 2013-03-06
  • 2018-10-29
  • 2013-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多