Удаление производных классов в контейнерах std::unique_ptr<Base>

StackOverflow https://stackoverflow.com//questions/24060497

Вопрос

Я немного запутался.По сути, у меня есть два разных менеджера ресурсов (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());
}
.

Demo

оба деструктора будут называться.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top