Здравствуйте, sumson, Вы писали:
S>да, забыл добавить...про виртуальный деструктор... его тоже можно убрать. S>если использовать бустовый shared_ptr. S>будет вопрос — поясню.
взято из Beyond the C++ Standard Library: An Introduction to Boost
The following example shows how to store shared pointers in a Standard Library container.
#include "boost/shared_ptr.hpp"
#include <vector>
#include <iostream>
class A {
public:
virtual void sing()=0;
protected:
virtual ~A() {};
};
class B : public A {
public:
virtual void sing() {
std::cout << "Do re mi fa so la";
}
};
boost::shared_ptr<A> createA() {
boost::shared_ptr<A> p(new B());
return p;
}
int main() {
typedef std::vector<boost::shared_ptr<A> > container_type;
typedef container_type::iterator iterator;
container_type container;
for (int i=0;i<10;++i) {
container.push_back(createA());
}
std::cout << "The choir is gathered: \n";
iterator end=container.end();
for (iterator it=container.begin();it!=end;++it) {
(*it)->sing();
}
}
The two classes, A and B, contain a single virtual member function sing. B derives publicly from A, and as you can see, the factory function createA returns a dynamically allocated instance of B wrapped in a shared_ptr<A>. In main, a std::vector containing shared_ptr<A> is filled with 10 elements, and finally sing is invoked on each element. Had we been using raw pointers as elements, the objects would need to be manually deleted. In the example, this deletion is automatic, because the reference count of each shared_ptr in the container is 1 as long as the vector is kept alive; when the vector is destroyed, the reference counters all go down to zero, and the objects are deleted. It is interesting to note that even if the destructor of A had not been declared virtual, shared_ptr would have correctly invoked the destructor of B!
пипец, я тоже в шоке.
Я знаю, как управлять Вселенной. И скажите, зачем же мне бежать за миллионом?!(c)
Здравствуйте, sumson, Вы писали:
S>It is interesting to note that even if the destructor of A had not been declared virtual, shared_ptr would have correctly invoked the destructor of B! S>пипец, я тоже в шоке.
Зачем тогда они объявили в А деструктор виртуальным, если всё так красиво?
Здравствуйте, sysenter, Вы писали:
S>Здравствуйте, sumson, Вы писали:
S>>It is interesting to note that even if the destructor of A had not been declared virtual, shared_ptr would have correctly invoked the destructor of B! S>>пипец, я тоже в шоке.
S>Зачем тогда они объявили в А деструктор виртуальным, если всё так красиво?
я привёл код из книги, теперь привожу со своими поправками для верности утверждения.
можете сами проверить, деструктор B вызывается...
class A {
public:
virtual void sing()=0;
};
class B : public A {
public:
virtual void sing() {
std::cout << "Do re mi fa so la";
}
~B(){std::cout << "~B()";}
};
boost::shared_ptr<A> createA() {
boost::shared_ptr<A> p(new B());
return p;
}
int main() {
typedef std::vector<boost::shared_ptr<A> > container_type;
typedef container_type::iterator iterator;
container_type container;
for (int i=0;i<10;++i) {
container.push_back(createA());
}
std::cout << "The choir is gathered: \n";
iterator end=container.end();
for (iterator it=container.begin();it!=end;++it) {
(*it)->sing();
}
}
Я знаю, как управлять Вселенной. И скажите, зачем же мне бежать за миллионом?!(c)
Здравствуйте, sumson, Вы писали:
S>я привёл код из книги, теперь привожу со своими поправками для верности утверждения. S>можете сами проверить, деструктор B вызывается...
Значит B удаляется через указатель типа B, а мог бы и через указатель типа A, что привело бы к утечке.
S>я привёл код из книги, теперь привожу со своими поправками для верности утверждения. S>можете сами проверить, деструктор B вызывается...
чем мне такая "книжная" магия не нравится, так это тем, что как оно работает понимает разве что автор книги. счастливый девелопер, окрыленный прочитанным, начинает это использовать не влезая под капот, потом другой девелопер, уже и не читавший ничего, добавляет что-то от себя и поддержка этого кода потом выливается в одно сплошное удовольствие, когда о проблемах простого delete'а приходится только мечтать
я вот с бустом поверхностно знаком, расскажешь мне (своими словами) как shared_ptr<A> понимает, что ему подали указатель на B и где он запоминает это дело, чтобы потом вызвать правильный деструктор? но без цитат, я посмотрел ту книжку, теперь хотелось бы оценить понимание предлагаемой магии.
Здравствуйте, sysenter, Вы писали:
S>Значит B удаляется через указатель типа B, а мог бы и через указатель типа A, что привело бы к утечке.
это всё понятно, он затем и нужен виртуальный деструктор в родительском классе чтобы таких утечек не было.
вопрос именно про использование shared_ptr вместо голых указателей.
Я знаю, как управлять Вселенной. И скажите, зачем же мне бежать за миллионом?!(c)
Здравствуйте, sysenter, Вы писали:
S>Здравствуйте, placement_new, Вы писали:
_>>Код посмотреть тяжело?
S>Предположение, определяет реальный тип через передачу параметра функции в которой инстанцирует шаблон с реальным типом.
template<class T> class shared_ptr
{
private:
// Borland 5.5.1 specific workaround
typedef shared_ptr<T> this_type;
public:
typedef T element_type;
typedef T value_type;
typedef T * pointer;
typedef typename boost::detail::shared_ptr_traits<T>::reference reference;
shared_ptr(): px(0), pn() // never throws in 1.30+
{
}
template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
{
boost::detail::sp_enable_shared_from_this( this, p, p );
}
видно, что сам shared_ptr параметризуется одним типом (class T) а принимает в конструктор другой тип (class Y)
а далее похоже запоминает или указатель на реальный тип(и при разрушении делает ему delete) или деструктор переданного класса(в чём я сомневаюсь).
можно ещё полазить в исходниках...времени нема)
Я знаю, как управлять Вселенной. И скажите, зачем же мне бежать за миллионом?!(c)
S> template<class Y>
S> explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
S> {
S> boost::detail::sp_enable_shared_from_this( this, p, p );
S> }
S>
Дык, как я и говорил делается через передачу функции, в функциях всегда известен реальный тип:
template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )
{
if( pe != 0 )
{
pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
}
}
Здравствуйте, sysenter, Вы писали:
S>Здравствуйте, sumson, Вы писали:
S>>
S>> template<class Y>
S>> explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
S>> {
S>> boost::detail::sp_enable_shared_from_this( this, p, p );
S>> }
S>>
S>Дык, как я и говорил делается через передачу функции, в функциях всегда известен реальный тип:
S>
S>template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )
S>{
S> if( pe != 0 )
S> {
pe->>_internal_accept_owner( ppx, const_cast< Y* >( py ) );
S> }
S>}
S>
спускаемся ещё глубже:
public: // actually private, but avoids compiler template friendship issues
// Note: invoked automatically by shared_ptr; do not call
template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
{
if( weak_this_.expired() )
{
weak_this_ = shared_ptr<T>( *ppx, py );
}
}
private:
mutable weak_ptr<T> weak_this_;
};
вот интересная строчка:
weak_this_ = shared_ptr<T>( *ppx, py );
читаем в книге:
template <class Y,class D> shared_ptr(Y* p,D d);
This constructor takes two arguments. The first is the resource that the shared_ptr should take ownership of, and the second is an object that is responsible for releasing that resource when the shared_ptr is destroyed. The stored resource is passed to the object as d(p).
значит есть шареный ресурс типа <T> и есть объект которые релизит этот ресурс типа <Y>.
в переводе на наш пример: шареный ресурс типа A, объект который будет релизить этот ресурс — типа B.
потому при разрушении shared_ptr вызывается деструктор класса B, хотя класс параметризован классом A.
Я знаю, как управлять Вселенной. И скажите, зачем же мне бежать за миллионом?!(c)
Здравствуйте, Nik_1, Вы писали:
N_>Удар кувалдой стоит $1, знать куда ударить стоит остальные $9999
Не пойму, вы — генератор случайных чисел или расказчик притч?
Здравствуйте, ov, Вы писали:
ov>я вот с бустом поверхностно знаком, расскажешь мне (своими словами) как shared_ptr<A> понимает, что ему подали указатель на B и где он запоминает это дело, чтобы потом вызвать правильный деструктор? но без цитат, я посмотрел ту книжку, теперь хотелось бы оценить понимание предлагаемой магии.
сейчас попробую сформулировать без цитат:
shared_ptr<A> это ресурс шареный, то есть ты его можешь передавать куда хошь, копировать присваивать, у него есть счётчик ссылок внутренний.
когда его создают ему подают в explicit конструктор указатель на B, он не производит преобразования B к A. Он просто считает переданный объект B как объект для очистки шареного ресурса shared_ptr<A> когда счётчик ссылок придёт в ноль и ресурс будет разрушаться.
То есть в моём понимании shared_ptr реально умный указатель...а не просто обёртка.
Я знаю, как управлять Вселенной. И скажите, зачем же мне бежать за миллионом?!(c)
S>сейчас попробую сформулировать без цитат:
спасибо. последние несколько сообщений в треде убедили меня, что иногда простой delete не такое уж и зло в продакшен такой код, да с таким пониманием как оно работает я бы не пускал полюбому. контрольный вопрос в голову (тут конечно это не важно, но чтоб жертва не мучалась): какие в итоге накладные расходы на хранение одного такого указателя?
S>То есть в моём понимании shared_ptr реально умный указатель...а не просто обёртка.
угу, пока эта магия не перешла на темную сторону силы через полгода активного использования.
Здравствуйте, ov, Вы писали:
S>>сейчас попробую сформулировать без цитат: ov>спасибо. последние несколько сообщений в треде убедили меня, что иногда простой delete не такое уж и зло в продакшен такой код, да с таким пониманием как оно работает я бы не пускал полюбому. контрольный вопрос в голову (тут конечно это не важно, но чтоб жертва не мучалась): какие в итоге накладные расходы на хранение одного такого указателя?
зла нет в delete или в каком ещё инструменте языка. зло есть в том как мы это используем и чем наделяем в программе.
я думаю что использование shared_ptr вместо голых указателей+delete — есть меньшее зло.
по поводу вашего продакшена:такое ощущение что ни вы ни те кто писали ваш продакшен код в глаза не видели смарт указателей.
по поводу накладных расходов...я думаю что вместо хранения одного указателя в shared_ptr мы будем хранить два указателя и плюс счётчик ссылок.
то есть вместо 4 байт указателя мы будем получать ещё минимум 8 байт дополнительных издержек памяти. ну плюс некоторая потеря производительности в процессе разыменовывания и вызова других ф-ций для доступа к указателю. существенные издержки или нет — каждый решает сам.
Я знаю, как управлять Вселенной. И скажите, зачем же мне бежать за миллионом?!(c)
S>зла нет в delete или в каком ещё инструменте языка. зло есть в том как мы это используем и чем наделяем в программе.
абсолютно правильно рассуждаешь.
S>я думаю что использование shared_ptr вместо голых указателей+delete — есть меньшее зло.
и здесь тоже. я не имею ничего против умных указателей, я наоборот за и именно об этом был пункт в чеклисте про delete. я против извратов с вызовом невиртуального деструктора через shared_ptr, указывающий на его предка.
S>по поводу вашего продакшена:такое ощущение что ни вы ни те кто писали ваш продакшен код в глаза не видели смарт указателей.
нет конечно. все я и видел, и использовал и сам делал ради интереса. я за подход без фанатизма: удалять все руками или городить страшные ужасы на бусте, которые удивительно как работают — вот это фанатизм. а просто std::auto_ptr, std::vector или бустовские поинтеры — да почему нет, если в тему. главное чтобы все участники команды понимали _как_ это работает.
S>существенные издержки или нет — каждый решает сам.
вопрос был не о существенности, а о понимании принципов работы. я об этом абзацем выше написал. если разработчик не понимает как инструмент работает внутри, стоит подумать или над продолжением его использования или над его устройством. иначе это потом может аукнуться. замечу, что тут речь идет исключительно об инструментах (контейнеры, хелперы всякие итп).