shared_ptr and unique_ptr: question about a specific case

82 Views Asked by At

I want to define two classes A and I in a such a way that their objects respect this kind of relationship:

i1 -----------a1
  |------a2
  |----a3
  • An instance of the class I points to zero, one, or many instances of the class A.

  • An instance of the class A points to only one instance of the class I.

  • An instance of class I can be without any instances of class A, but an instance of class A must have an instance of class I "attached".

In order to statisfy these conditions, I declared the two classes as follows:

class I;
class A
{
    private:
        std::string as;
        std::shared_ptr<I> iA;        
    public:
        A(std::string aTxt);
        A();
        ~A();
};

class I
{
   private:
       std::string ip;
       std::vector<std::unique_ptr<A>> as;
       friend class A;
   public:
       I(std::string i);
       ~I();
};

And in the source file, I defined the two classes in this way:

A::A(std::string aText)
{
    as = aText;
}

A::A()
{
    as = "";
}

A::~A()
{
}

I::I(std::string i)
{
    ip = i;
}

I::~I()
{

}

Questions:

  1. When the instance i of the class I is deleted, all the attached instances a of the class A have to be removed. How to set this mechanism in the destructor of I?

  2. When an instance a of the class A is deleted, the instance i of class I that it points to is still pointed by other instances a of the class A. How to be sure that once all the instances a of the classes A are deleted, the instance i of the class I can still exist, even if no instances of A point to it?

2

There are 2 best solutions below

1
Remy Lebeau On BEST ANSWER

When the instance i of the class I is deleted, all the attached instances a of the class A have to be removed. How to set this mechanism in the destructor of I?

The vector of unique_ptrs will handle that for you automatically. You don't need to write any extra code for it. When an instance of I is destroyed, the destructor of the vector instance will call the destructor of the unique_ptr instances, which will destroy the A instances.

When an instance a of the class A is deleted, the instance i of class I that it points to is still pointed by other instances a of the class A. How to be sure that once all the instances a of the classes A are deleted, the instance i of the class I can still exist, even if no instances of A point to it?

A should not have a smart pointer (shared_ptr) to I at all, as A does not control I's lifetime. A non-owning raw pointer will suffice in this case.

Try this:

class I;

class A
{
    private:
        std::string as;
        I* iA;        
    public:
        A(I* i, std::string aTxt = "");
};

class I
{
   private:
       std::string ip;
       std::vector<std::unique_ptr<A>> as;
       friend class A;
   public:
       I(std::string i);
       void add(std::string aTxt);
};
A::A(I* i, std::string aText)
{
    iA = i;
    as = aText;
}

I::I(std::string i)
{
    ip = i;
}

void I::add(std::string aTxt)
{
    as.push_back(std::make_unique<A>(this, aTxt));
}
1
Yksisarvinen On

The way you describe it, the ownership relation is simple - I is sole owner of A object(s). A does not have any ownership over I and moreover, it is defined to always have lifetime shorter than the I objects that owns it.

So one solution would be to have I keep a std::vector<std::unique_ptr<A>> (or preferably std::vector<A> if A is not polymorphic) and A to accept a raw pointer to I in constructor.

class I;
class A
{
    private:
        I* iA;        
    public:
        explicit A(I* ownerI) : iA{ownerI} {}
        // no default constructor - you don't want to create invalid objects with no `iA` pointer
        // no user declared destructor - default one is perfectly fine
};

class I
{
   private:
        std::vector<A> as;
        std::vector<std::unique_ptr<A>> polymorphicAs;
   public:
        // no user-declared destructor - default one will do correct things with either 
        //std::vector<A> and std::vector<std::unique_ptr<A>>

        void foo() {
            as.emplace_back(this);
            polymorphicAs.push_back(std::make_unique<A>(this));
        }
};