В качестве еще одной иллюстрации к предыдущей заметке про "
магию shared_ptr", в ответ один из комментариев - небольшой пример: создадим простую иерархию классов, где базовый класс не имеет виртуального деструктора.
struct A { ~A() { cout << "~A()" << endl; } };
struct B : public A { ~B() { cout << "~B()" << endl; } };
А теперь создадим объект класса B и поместим его в контейнер указателей на A:
{
auto_ptr<B> b(new B);
ptr_vector<A> x;
x.push_back(b);
}
{
auto_ptr<B> b(new B);
vector< shared_ptr<A> > x;
x.push_back(b);
}
В случае контейнера, хранящего только указатели (в данном случае - boost::ptr_vector) - получим при разрушении объекта вызов только ~A(), а в случае с shared_ptr будет вызван ~B(), так как shared_ptr
немного умнее обычного указателя.
6 комментариев:
Ну ты же знаешь, что за такое надо отрывать уши. Как (!!!) можно полиморфно использовать указатель на предка при таком наследовании?
Ты не поверишь, но не всегда нужен полиморфизм.
Ох-хо-хо.. Я вовсе не пытаюсь тебе оскорбить, но дело в том, что С++ мстит очень жестко. В данном случае ты привел неудачный пример использования boost::shared_ptr.
Почитай "Стандарты кодирования" Александреску и Саттера. Если класс не имеет виртуального деструктора - он НЕ предназначен для наследования. Если ты все же хочешь воспользоваться функциональностью такого класса - инкапсулируй его. Ты считаешь в примере нет "граблей"?
Возможно я немного резко высказался, но оскорблять уж точно не входило в мои планы. Но на резкий комментарий - резкий ответ, ты уж извини.
По теме. Наглядный пример: наследники ATL::CWindow. Если поместить их в контейнер как shared_ptr<ATL::CWindow>, то для кода, который будет использовать этот контейнер они - лишь обертка над HWND. Полиморфизм у них реализован через WindowProc() и отлично работает без виртуальных методов. У некоторых объектов будут и виртуальные методы в понимании C++ (у наследников от CWindowImpl<..., CWindow, ...>), у других (CEdit) - нет. И "снаружи" не стоит об этом задумываться: CWindow сработает как обертка HWND, деструктор shared_ptr вызовет нужный деструктор для каждого конкретного объекта. Где грабли?
А как тогда можно воспользоваться вектором x? Как ты определяешь объект какого типа содержится в i-м элементе? Или ты используешь vector< shared_ptr < > > только для удаления их в некоторый момент? А имеет ли вообще смысл хранить контролы в массиве? Своеобразный сборщик мусора?
Пользоваться довольно просто:
BOOST_FOREACH(boost::shared_ptr<CWindow> &Window, Windows)
{
Window->SetWindowText(...);
Window->ShowWindow(...);
Window->...;
}
Отправить комментарий