darune
darune

Reputation: 11012

Lifetime problems with RAII style encapsulation when the "resource manager" is not global

RAII works great when allocating from global ressource / OS. Almost all the cases in the standard library uses some type of global resource, but suppose this is not the case. (An exception is of course custom allocators for containers, but this is not a very good example in my view of code you want in production (basicly just uses pointers everywhere)).

// obj_rep could for example contain an id number or handle/pointer of some kind, does not //matter so much

struct Manager() {
  obj_rep allocate();
  void deallocate(obj_rep);
}

struct MyObject {
  MyObject (Manager& m) : mng {m} {
    //call allocate with a manager object
    rep = mng.allocate();
  }

  ~MyObject () {
    //call deallocate with the same manager object
    mng.deallocate(rep);
  }

  //TODO: implement rule of 3/5 here

private:
  obj_rep rep;
  Manager& mng;
}

In the above example (and my current implementation) I stored a reference (could also be pointer) to a Manager - this then requires the user to manage lifetime correctly. But is there a better way ? I know shared_ptr / weak_ptr could be used, but is there a way to reverse the design somehow to get rid of lifetime issue and still have the RAII encapsulation ?

Smart/weak pointers and raw pointers should, for the most part, be avoided in library level code at the interface level (in c++).

Upvotes: 1

Views: 159

Answers (2)

AndrewBloom
AndrewBloom

Reputation: 2438

First of all, using a reference for a class member is strongly discouraged by the C++ core guidelines ("Note that using a reference member is almost always wrong").

If all the objects are scoped objects, which is usually best practice (C++ core guideline R.5), the manager has to be created on the outer scope or first at the same scope of the MyObject objects. You can enforce being scoped objects poisoning the operator new.

If the MyObject objects can be dynamically allocated, you can use shared_pointer or any similar technique. Or you can enforce ownership of MyObject objects by the Manager (or a third object that owns both), and the latter will be in charge of creating and destroying MyObject objects using RAII.

Upvotes: 1

wohlstad
wohlstad

Reputation: 29009

You mentioned in the title that the Manager in not global.
Therefore I assume that somewhere you have a variable/member like Manager theManager.

In order keep the RAII behavior of MyObject and at the same time ensure automatic lifetime management of the Manager instance, you can use std::shared_ptr in the following way:

  1. The instance of the Manager mentioned above should become a std::shared_ptr<Manager> theManager.
  2. The mng member of MyObject should become a std::shared_ptr<Manager> mng (and the parameter of the MyObject constructor should change accordingly).
  3. When you create a MyObject instance pass the theManager object (and the destructor will use mng in an RAII style).

The lifetime of the Manager will be automatically managed by the std::shared_ptr - it will be destroyed when theManager is destroyed and it is no longer referenced by any MyObject .

Upvotes: 2

Related Questions