我有一些(在我看来)相当具体的所有权要求:我有一个基本上解释一个双精度数组的类是一种特定的方式(一些相当大的矩阵的串联),并希望与一个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. */
};
禁止复制和移动,以便不会意外地创建或移动一个比缓冲区本身更长的地方。当然,通过直接将指针传递到某处可以有意识地创建这样的实例,但是可以在源代码中更容易地发现它。
我的问题的第一部分:让“Foo
增强了缓冲区的所有权”是Foo
的子类是否有意义?
Foo
的所有操作都可以通过拥有 - Foo
,另外,拥有 - Foo
可以自由复制和移动。它闻起来像Liskov替代原则是满意的。能够以同样的方式处理拥有 - Foo
和Foo
而无需在拥有一堆方法中编写 - Foo
委托给成员变量非常舒适
另一方面,可能会有所有者 - 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_;
};
我的第二部分问题:这是多重和私有继承的好例子吗?我在某个地方拍摄自己的脚吗?
请注意,如果Foo
不是成员,则std::unique_ptr
不能成为成员,因为它需要在 Foo
之前启动。
答案 0 :(得分:1)
我这样做的方法是将所有权问题推向更远的地方。 Foo有一个缓冲区,并知道如何在销毁时清理缓冲区。例如,std :: shared_ptr有一个可用于此目的的destroy回调。这显示了智能指针知道如何删除此特定实例的已接受模式。
实际上,您应该有一个可能的共享缓冲区,以便跟踪总拥有量。隐含地将它编程为“不是我”与其他地方知道发生了什么是相当脆弱的。
“检查所有权标志”只是“我是最后一个/唯一所有者”的特例,它具有您可以使用的一般强大实现。
在你提到的扭曲中,拥有缓冲区的C代码如何与你的类的生命周期协调?这听起来很糟糕,让你的班级知道它不拥有缓冲区(以一种封装良好的方式)不会改变C代码的问题,因为它不知道你的实例何时完成它。