Подтвердить что ты не робот

Удаляет ли работа с указателями на базовый класс?

Вам нужно передать удаление того же указателя, который был возвращен новым, или вы можете передать ему указатель на один из базовых типов классов? Например:

class Base
{
public:
    virtual ~Base();
    ...
};

class IFoo
{
public:
    virtual ~IFoo() {}
    virtual void DoSomething() = 0;
};

class Bar : public Base, public IFoo
{
public:
    virtual ~Bar();
    void DoSomething();
    ...
};

Bar * pBar = new Bar;
IFoo * pFoo = pBar;
delete pFoo;

Конечно, это значительно упрощается. Я действительно хочу создать контейнер, полный boost:: shared_ptr, и передать его на некоторый код, который удалит его из контейнера, когда он будет завершен. Этот код ничего не знает о реализации Bar или Base и будет полагаться на подразумеваемый оператор delete в destructor shared_ptr, чтобы делать правильные вещи.

Может ли это работать? Моя интуиция говорит "нет", поскольку указатели не будут иметь одинаковый адрес. С другой стороны, dynamic_cast < Bar * > должен работать, поэтому где-то компилятор хранит достаточно информации, чтобы понять это.


Спасибо за помощь, всем, кто ответил и прокомментировал. Я уже знал важность виртуальных деструкторов, как показано в моем примере; увидев ответ, я немного подумал и понял, что целая причина для виртуального деструктора - это точный сценарий. Таким образом, он должен был работать. Я был брошен из-за отсутствия видимых средств преобразования указателя обратно в оригинал. Немного больше мышления побудило меня поверить, что есть невидимые средства, и я предположил, что деструктор возвращает истинный указатель на удаление, чтобы освободить. Исследование скомпилированного кода из Microsoft VС++ подтвердило мое подозрение, когда я увидел эту строку в ~ Base:
mov eax, DWORD PTR _this$[ebp]

Отслеживание ассемблера показало, что это был указатель, передаваемый функции удаления. Тайна решена.

Я установил пример добавления виртуального деструктора в IFoo, это был простой надзор. Еще раз спасибо всем, кто указал на это.

4b9b3361

Ответ 1

Да, он будет работать, , если и только если деструктор базового класса является виртуальным, что вы сделали для базового класса Base, но не для базового класса IFoo. Если деструктор базового класса является виртуальным, то при вызове operator delete в указателе базового класса он использует динамическую диспетчеризацию, чтобы выяснить, как удалить объект, просмотрев деструктор производного класса в таблице виртуальных функций.

В случае множественного наследования он будет работать только в том случае, если базовый класс, который вы удаляете, имеет виртуальный деструктор; это нормально, если другие базовые классы не имеют виртуального деструктора, но только если вы не пытаетесь удалить любые производные объекты с помощью других указателей базового класса.

Ответ 2

Это не относится к вашему приведенному примеру, но поскольку вы упомянули, что вы действительно заинтересованы в поведении shared_ptr при удалении принадлежащего ему объекта, вам может быть интересно использовать shared_ptr 'deleter'.

Если объекту, принадлежащему shared_ptr, требуется специальная обработка при удалении, вы можете указать "deleter" для любого конкретного shared_ptr<>. Делетер не является частью этого типа, он является атрибутом экземпляра shared_ptr<>, поэтому ваш контейнер объектов shared_ptr<> может иметь некоторые объекты с разными удалениями. Здесь, что Boost docs говорят об shared_ptr<> deleter:

Пользовательские деаллокаторы допускают factoryфункция возвращает shared_ptr в изолировать пользователя от его памяти стратегии распределения. Поскольку deallocator не является частью типа, изменение стратегии распределения не прерывать источник или двоичный совместимости и не требует перекомпиляция клиента. Например, "de-op" deallocator полезен, когда возвращая a shared_ptr к статически выделенный объект и другие варианты разрешите использовать shared_ptr в качестве обертка для другого умного указателя, облегчая взаимодействие.

Было бы самым чистым, если бы вы могли изменить IFoo, чтобы иметь виртуальный деструктор, поскольку вы планируете удалять объекты, которые являются его подклассами с помощью ссылки или указателя IFoo. Но если вы застряли с IFoo, который не может быть исправлен, то если вы хотите использовать shared_ptr<IFoo> в своем контейнере, но если он указывает на Bar, вы можете создать экземпляр shared_ptr с удалением который выполняет downcast для Bar*, а затем выполняет операцию удаления. Downcasts считаются плохой формой, но этот метод может быть тем, что вы могли бы использовать в привязке.