質問

I have some (in my opinion) fairly specific ownership requirements: I have a class that basically interprets an array of doubles is a specific way (a concatenation of some fairly large matrices), and want to communicate with a C library that interprets then in another way (a very long mathematical vector). In some cases, I want to interpret a pointer that is passed to a callback by the C library, that is, without taking ownership. In that case, copying would be very impractical. In other cases, I want to allocate the buffer myself, and pass it to the C library myself. In that case, my code owns buffer.

I created a "building block" that interprets the double array as the matrices (with boost::numeric::ublas::shallow_array_adaptor, but that is mostly irrelevant), like this:

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. */
};

Copying and moving is forbidden so that and instance cannot accidentally be created or moved somewhere which outlives the buffer itself. Of course, deliberate creation of such instances is possible by directly passing the pointer somewhere but can be spotted more easily in the source.

The first part of my question: Does it make sense to let "Foo enhanced with ownership of the buffer" be a subclass of Foo?

Every operation of Foo is possible with the owning-Foo, additionally, owning-Foo can be freely copied and moved. It smells like the Liskov substitution principle is satisfied. Being able to handle owning-Foo and Foo the same way syntactically without writing a bunch of methods in owning-Foo that delegate to a member variable is very comfortable.

On the other hand, there could be owner-of-Foo instead, that deals with the ownership and nothing else, and contains an instance of Foo that may be accessed from outside, providing better separation of concerns.

I implemented owning-Foo like this:

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_;
};

My second part of my question: Is this a good case for multiple and private inheritance? Am I shooting myself in the foot somewhere?

Note that if Foo is not a member, than std::unique_ptr can neither be a member, because it needs to be initilized before Foo.

役に立ちましたか?

解決

The way I do it is push the ownership issue farther down. Foo has a buffer, and knows how to clean up that buffer when it is destroyed. The std::shared_ptr has a destroy callback that can be used for that purpose, for example. This shows the accepted pattern of having the smart pointer know how this particular instance is to be deleted.

Really, you should have a possibly shared buffer so the total ownership is tracked. Implicitly programming it to be "not me" with some other place knowing what's going on is rather brittle.

"checking ownership flags" is just a special case of "am I the last/only owner" which has a general robust implementation you can use.

In the twist you mention, how does the C code that owns the buffer coordinate with your class's lifetime? It sounds bad, and having your class know it doesn't own the buffer (in a well-encapsulated way) doesn't change the problem of the C code not knowing when your instance is done with it.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top