Мы с мистером Лидстремом поссорились :)
Г-н Лидстрём утверждает, что конструкция shared_ptr<Base> p(new Derived);
не требует наличия виртуального деструктора в Base:
Армен Цирунян : «Правда? Будет ли shared_ptr правильно очищаться? Не могли бы вы в этом случае продемонстрировать, как этот эффект может быть реализован?»
Даниэль Лидстрём : « shared_ptr использует свой собственный деструктор для удаления экземпляра Concrete. В сообществе C ++ он известен как RAII. Я советую вам узнать все, что можно о RAII. Это значительно упростит программирование на C ++ при использовании RAII во всех ситуациях ».
Армен Цирунян : «Я знаю о RAII, и я также знаю, что в конечном итоге деструктор shared_ptr может удалить сохраненный px, когда pn достигнет 0. Но если у px есть указатель статического типа и указатель
Base
динамического типа наDerived
, то, еслиBase
нет виртуального деструктора, это приведет к неопределенному поведению. Исправьте меня, если я ошибаюсь. "Даниэль Лидстрём : « shared_ptr знает, что статический тип - это Concrete. Он знает это, так как я передал его в его конструкторе! Это немного похоже на магию, но я могу вас заверить, что это сделано по дизайну и очень красиво».
Итак, судите нас. Как возможно (если это возможно) реализовать shared_ptr, не требуя, чтобы полиморфные классы имели виртуальный деструктор? заранее спасибо
источник
shared_ptr<void> p(new Derived)
он также уничтожитDerived
объект своим деструктором, независимо от того, есть онvirtual
или нет.shared_ptr<T>( (T*)new U() )
wherestruct U:T
не будет делать то, что нужно (и это можно легко сделать косвенно, например, функция, которая принимаетT*
и передает aU*
)Ответы:
Да, таким образом можно реализовать shared_ptr. Boost делает, и стандарт C ++ 11 также требует такого поведения. В качестве дополнительной гибкости shared_ptr управляет не только счетчиком ссылок. Так называемый удалитель обычно помещается в тот же блок памяти, который также содержит счетчики ссылок. Но самое интересное в том, что тип этого средства удаления не является частью типа shared_ptr. Это называется «стиранием типа» и в основном представляет собой тот же метод, который используется для реализации «полиморфных функций» boost :: function или std :: function для сокрытия фактического типа функтора. Чтобы ваш пример заработал, нам понадобится шаблонный конструктор:
template<class T> class shared_ptr { public: ... template<class Y> explicit shared_ptr(Y* p); ... };
Итак, если вы используете это со своими классами Base и Derived ...
class Base {}; class Derived : public Base {}; int main() { shared_ptr<Base> sp (new Derived); }
... шаблонный конструктор с Y = Derived используется для создания объекта shared_ptr. Таким образом, конструктор имеет возможность создать соответствующий объект-удалитель и счетчики ссылок и сохраняет указатель на этот блок управления в качестве элемента данных. Если счетчик ссылок достигает нуля, для удаления объекта будет использован ранее созданный и поддерживающий производные средства удаления.
В стандарте C ++ 11 об этом конструкторе (20.7.2.2.1) говорится следующее:
А для деструктора (20.7.2.2.2):
(выделение жирным шрифтом принадлежит мне).
источник
the upcoming standard also requires this behaviour
: (а) Какой стандарт и (б) вы можете дать ссылку (на стандарт)?add a comment
. ИМО, большеBoost does this
чемthe Standard requires
. Я не думаю, что Стандарт требует этого из того, что я понимаю. Говоря о примере @sellibitze «sshared_ptr<Base> sp (new Derived);
, требует отconstructor
просто попроситьdelete Derived
быть четко определены и хорошо сформированы. Для спецификацииdestructor
существует такжеp
, но я не думаю, что он относится кp
в спецификацииconstructor
.Когда shared_ptr создается, он сохраняет внутри себя объект удаления . Этот объект вызывается, когда shared_ptr собирается освободить указанный ресурс. Поскольку вы знаете, как уничтожить ресурс в момент построения, вы можете использовать shared_ptr с неполными типами. Тот, кто создал shared_ptr, сохранил там правильный удалитель.
Например, вы можете создать собственный удалитель:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed. shared_ptr<Base> p(new Derived, DeleteDerived);
p вызовет DeleteDerived для уничтожения указанного объекта. Реализация делает это автоматически.
источник
shared_ptr
в качестве атрибута.Просто,
shared_ptr
использует специальную функцию удаления, которая создается конструктором, который всегда использует деструктор данного объекта, а не деструктор Base, это небольшая работа с метапрограммированием шаблона, но оно работает.Что-то такое
template<typename SomeType> shared_ptr(SomeType *p) { this->destroyer = destroyer_function<SomeType>(p); ... }
источник