Initializing a unique_ptr in constructor of base class properly

2.1k Views Asked by At

I try to pass an std::unique_ptr to an inherited class, which will forward it to the base class constructor (using an constructor initializer list). If the base class constructor receives an nullptr an default-object should be constructed and assigned to the std::unique_ptr member-variable from my base-class. But somehow I get an AccessViolation, if I try to access any elements from thestd::unique_ptr anywhere (because it's somehow still an nullptr - even if that should be impossible at this time).

Any ideas whats going wrong here?

#include <iostream>
#include <memory>

class C{
public:
  int x;
};

class A{
public:
  A(std::unique_ptr<C> c) : c(std::move(c)){
    if(c == nullptr){
      c = std::unique_ptr<C>(new C);
      c->x = 1;
    }
  }
  void print(){
    std::cout << c->x << std::endl;
  }
private:
  std::unique_ptr<C> c;
};

class B : public A{
public:
  B(std::unique_ptr<C> c) : A(std::move(c)){
  }
};

int main(int argc, char* argv[]){
  B b(nullptr);
  b.print();
  return 0;
}

https://ideone.com/fHvYqe

3

There are 3 best solutions below

0
On BEST ANSWER

In A::ctor you are using variable c but it is not ref to class member A::c but ref to local variable c which is ctor parameter. So after ctor exit the A::c will be nullptr, so you can't dereference it in A::print function.

  A(std::unique_ptr<C> c) : c(std::move(c)){
    if(c == nullptr) {               // here c is ctor parameter (local variable)
      c = std::unique_ptr<C>(new C); // A:c is still nullptr
      c->x = 1;                      // 
    }
  }

Possible solution is to make different names for local variable c name and A::c, e.g. A::m_c.

0
On

If you don't want to do any additional work in the constructor and providing all possible constructors of the base is not a problem use inheriting constructors.

class B : public A {
  using A::A;
};
0
On

What happens is that you named variables very badly.

The constructor's parameter is named the same as the member variable. Because of that, only constructor's variable is created in the constructor's body, and member variable is assigned in the initialization list whatever you pass (nullptr in your case).

To fix the problem, rename the constructor's parameter :

  A(std::unique_ptr<C> c1) : c(std::move(c1)){
    if(c == nullptr){
      c = std::unique_ptr<C>(new C);
      c->x = 1;
    }
  }