1

我有一些(在我看来)相当具体的所有权要求:我有一个基本上解释双精度数组的类是一种特定的方式(一些相当大的矩阵的串联),并且想与一个 C 库进行通信,然后在另一种方式(一个很长的数学向量)。在某些情况下,我想解释由 C 库传递给回调的指针,即不获取所有权。在这种情况下,复制将是非常不切实际的。在其他情况下,我想自己分配缓冲区,然后自己将其传递给 C 库。在这种情况下,我的代码拥有缓冲区。

我创建了一个“构建块”,将双数组解释为矩阵(使用boost::numeric::ublas::shallow_array_adaptor,但这几乎无关紧要),如下所示:

class Foo {
 public:
  explicit Foo(double *buffer);
  Foo(const Foo &) = delete;
  Foo(Foo &&) = delete;
  Foo &operator=(const Foo &) = delete;
  /* Some accessors. */
 protected:
  Foo &operator=(Foo &&) = default;
 private:
  /* Some things that store pointers into the buffer. */
};

复制和移动是被禁止的,这样实例就不会被意外地创建或移动到超出缓冲区本身寿命的地方。当然,可以通过直接将指针传递到某处来故意创建此类实例,但可以更容易地在源代码中发现。

我的问题的第一部分:让“通过缓冲区所有权增强”成为 的子类是否有意义?FooFoo

拥有- 的每一个操作Foo都是可能的Foo,另外,拥有-Foo可以自由复制和移动。闻起来像是满足了 Liskov 替换原则。能够以相同的语法方式处理拥有FooFoo而无需在拥有中编写一堆方法,Foo将委托给成员变量是非常舒服的。

另一方面,可能有 owner-of- Foo,它只处理所有权而不是其他任何东西,并且包含一个Foo可以从外部访问的实例,从而提供更好的关注点分离。

我实现了拥有 -Foo像这样:

class OwningFoo : private std::unique_ptr<double[]>, public Foo {
 public:
  explicit OwningFoo(std::size_t size)
      : std::unique_ptr<double[]>(new double[size]),
        Foo(std::unique_ptr<double[]>::get()), size_(size) {
  }
  /* Implementation of copy and move constructors and
   * assignment operators redacted. */
  OwningFoo(const OwningFoo &);
  OwningFoo(OwningFoo &&);
  OwningFoo &operator=(const OwningFoo &);
  OwningFoo &operator=(OwningFoo &&);
 private:
  std::size_t size_;
};

我的问题的第二部分:这是多重和私有继承的好案例吗?我在某处射自己的脚吗?

注意 ifFoo不是成员,thanstd::unique_ptr也不能是成员,因为它需要在之前 Foo初始化。

4

1 回答 1

1

我这样做的方式是将所有权问题推得更远。Foo 有一个缓冲区,并且知道在该缓冲区被销毁时如何清理该缓冲区。例如,std::shared_ptr 有一个可用于该目的的销毁回调。这显示了让智能指针知道如何删除这个特定实例的公认模式。

实际上,您应该有一个可能共享的缓冲区,以便跟踪总所有权。将其隐式编程为“不是我”,而其他地方知道发生了什么是相当脆弱的。

“检查所有权标志”只是“我是最后一个/唯一的所有者”的一个特例,它具有您可以使用的一般强大的实现。

在您提到的扭曲中,拥有缓冲区的 C 代码如何与您的类的生命周期相协调?这听起来很糟糕,并且让您的类知道它不拥有缓冲区(以良好封装的方式)并不会改变 C 代码不知道您的实例何时完成的问题。

于 2014-09-16T06:02:31.693 回答