Правильная инициализация unique_ptr в конструкторе базового класса

Я пытаюсь передать std::unique_ptr в унаследованный класс, который будет перенаправлять его в конструктор базового класса (используя список инициализатора конструктора). Если конструктор базового класса получает nullptr объект по умолчанию должен быть создан и назначен std::unique_ptr переменная-член из моего базового класса. Но каким-то образом я получаю AccessViolation, если я пытаюсь получить доступ к любым элементам изstd::unique_ptr в любом месте (потому что это как-то еще nullptr - даже если это должно быть невозможно в это время).

Есть идеи, что здесь происходит не так?

#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 ответа

Решение

В A::ctor вы используете переменную c но это не ссылка на члена класса A::c но ссылка на локальную переменную c который является параметром ctor. Поэтому после выхода из ctor A::c будет nullptrтак что вы не можете разыменовать его в A::print функция.

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

Возможное решение - сделать разные имена для локальной переменной c имя и A::cнапример, A::m_c,

Если вы не хотите выполнять какую-либо дополнительную работу в конструкторе, и предоставление всех возможных конструкторов базы не является проблемой, используйте наследующие конструкторы.

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

Что происходит, так это то, что вы назвали переменные очень плохо.

Параметр конструктора называется так же, как переменная-член. По этой причине в теле конструктора создается только переменная конструктора, а переменная-член присваивается в списке инициализации независимо от того, что вы передаете (nullptr в вашем случае).

Чтобы решить проблему, переименуйте параметр конструктора:

  A(std::unique_ptr<C> c1) : c(std::move(c1)){
    if(c == nullptr){
      c = std::unique_ptr<C>(new C);
      c->x = 1;
    }
  }
Другие вопросы по тегам