Уникальная инициализация указателя - PullRequest
0 голосов
/ 20 марта 2020

Я все еще новичок в c ++, извиняюсь, если это очевидно, но я не смог найти хорошего ответа после долгих поисков.
I w * sh Я мог бы написать следующий код.

class Test {
public:
    Test();

private:
    std::unique_ptr<Dummy> m_Dummy;
};
Test::Test() {
    auto data = // generate some data here
    m_Dummy = std::make_unique<Dummy>(data);
}

Что происходит:
Оператор присваивания для m_Dummy вызывает unique_ptr::reset,
, который вызывает delete для указателя,
, который вызывает деструктор m_Dummy,
и запуск деструктора создает segfault, потому что он никогда не был инициализирован.

Правильный способ был бы инициализировать его в списке инициализации конструктора.
Но тогда я не смог бы передать нужные данные .

Test::Test() : m_Dummy{std::make_unique<Dummy>(data)} { // Don't have time to generate data
}

Я не уверен, как сделать это чище.
Моя текущая идея состоит в том, чтобы заменить Dummy конструктором по умолчанию, а затем initialize функцией, которая принимает данные.
хотя неправильно.

Есть ли более понятный способ справиться с этим?
Что обычно делается с умными указателями, которые нуждаются в параметрах и также должны быть членами класса?

Спасибо,
Натан

Редактировать:
Из приведенного ниже ответа, возможно, где-то в моем коде совершенно другая проблема, вызывающая это.
Это вызов из отладчика непосредственно перед Segfault брошен.

Dummy::~Dummy Dummy.cpp:24
std::default_delete<Dummy>::operator() unique_ptr.h:78
std::unique_ptr<Dummy, std::default_delete<Dummy> >::reset unique_ptr.h:371
std::unique_ptr<Dummy, std::default_delete<Dummy> >::operator= unique_ptr.h:278
Test::Test Test.cpp:42
std::make_unique<Test, int&, double, double> unique_ptr.h:821
World::World World.cpp:25
Application::Run Application.cpp:77
main main.cpp:10
__libc_start_main 0x00007fbd47bbdb97
_start 0x0000555e1df657ea

Edit2:
Проблема заключалась в том, что в процессе создания моих данных я повреждала свою память, а Dummy просто оказался жертвой. Мое оригинальное предложение по созданию unique_ptr работает сейчас.

Спасибо

Ответы [ 2 ]

2 голосов
/ 20 марта 2020

Что происходит:
Оператор присваивания на m_Dummy вызывает unique_ptr::reset, который вызывает delete для указателя, который вызывает деструктор m_Dummy, а запуск деструктора создает ошибку сегмента, потому что он никогда не инициализировался.

Это НЕ то, что происходит при нормальных условиях.

m_Dummy не инициализируется явно в Test конструктор, поэтому он получает неявно взамен созданный по умолчанию, а его конструктор по умолчанию устанавливает свой удерживаемый указатель на nullptr.

Когда unique_ptr содержит nullptr, reset() не работает Совершенно безопасно присвоить unique_ptr, который удерживает nullptr.

Даже если reset() не был запретным, совершенно безопасно звонить delete на nullptr .

Тем не менее, ЕДИНСТВЕННЫЙ способ вызывать деструктор Dummy при назначении на m_Dummy - это когда m_Dummy не удерживает nullptr. Чтобы это произошло в конструкторе Test, который вы показали, m_Dummy должен быть в недопустимом состоянии, либо потому что:

  • конструктор Test был вызван недопустимым память (маловероятно, если только вы не используете placement-new)

  • ваш код для инициализации data или даже сам конструктор Dummy повреждает случайную память, а m_Dummy является невольной жертвой этой коррупции (более вероятно).

1 голос
/ 20 марта 2020

Создать метод static для генерации данных?

class Test {
public:
    Test();

private:
    static Dummy makeConstructionData()
    {
      return Dummy();
    }
    std::unique_ptr<Dummy> m_Dummy;
};

Затем вы можете сделать:

Test::Test() : m_Dummy{std::make_unique<Dummy>(makeConstructionData())} { 
}
...