Удаление производных классов в контейнерах std::unique_ptr<Base>
-
26-12-2019 - |
Вопрос
Я немного запутался.По сути, у меня есть два разных менеджера ресурсов (AudioLibrary и VideoLibrary), которые наследуются от общего класса BaseLibrary.Этот базовый класс содержит ссылки как на аудио, так и на видео.И аудио, и видео наследуются от родительского класса Media.
Я храню данные на карте, заполненной unique_ptr.Но, к моему удивлению, я обнаружил, что когда эти указатели в конечном итоге удаляются с помощью .erase, вызывается только базовый деструктор для Media.
Наверное, я что-то пропустил, но думал, что компилятор/библиотека времени выполнения будет знать, что она указывает либо на видео, либо на аудио, и вызовет ее деструктор.
Кажется, это не так.Я вынужден сделать что-то вроде этого, чтобы вернуть все свои ресурсы:
void AudioLibrary::deleteStream( const std::string &pathFile )
{
auto baseStream = mStreams[ pathFile ].release();
mStreams.erase( pathFile );
// Re-cast!
auto aStream = static_cast<AudioStream*>( baseStream );
delete aStream;
}
Это нормальное поведение?
Обновлять:
С вами все в порядке — дело, конечно, в недостающей «виртуальности» деструктора.Думаю, в последнее время я все меньше и меньше задумывался о том, что такое виртуальный подход и наследование, и как бы терялся в его функциональности, а не в самих концепциях.Со мной это случается иногда.
Решение
Средство удаления по умолчанию для unique_ptr<T>
это удачное название default_delete<T>
.Это лицо без гражданства функтор, который вызывает delete
на его T *
аргумент.
Если вы хотите, чтобы правильный деструктор вызывался при unique_ptr
чтобы базовый класс был разрушен, вы должны либо использовать виртуальный деструктор, либо захватывать производный тип в средстве удаления.
Вы можете сделать это довольно легко, используя средство удаления указателя функции и лямбду без захвата:
std::unique_ptr<B, void (*)(B *)> pb
= std::unique_ptr<D, void (*)(B *)>(new D,
[](B *p){ delete static_cast<D *>(p); });
Конечно, это означает, что вам нужно добавить аргумент шаблона для вашего средства удаления ко всем использованиям unique_ptr
.Инкапсуляция этого в другом классе могла бы быть более элегантной.
Альтернативой этому является использование shared_ptr
, как делает захватывать производный тип, пока вы создаете производный тип shared_ptr
с использованием std::shared_ptr<D>(...)
или, предпочтительно, std::make_shared<D>(...)
.
Другие советы
Это связано с тем, что вы не объявили вашу генеракодицетагкод деструктора GenaCodicCode.Как вы можете видеть, если вы сделаете, например:
struct Media {
virtual ~Media() = default;
};
struct AudioLibrary : Media {};
struct VideoLibrary : Media {};
int main() {
std::map<int, std::unique_ptr<Media>> map;
map[0] = std::unique_ptr<Media>(new AudioLibrary());
map[1] = std::unique_ptr<Media>(new VideoLibrary());
}
.
оба деструктора будут называться.