Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Чтоб те, кто будут работать с его иерархией, прочувствовали всю прелесть процесса отладки.
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Как вариант — не предназначал класс для полиморфного удаления.
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия).
Но, если изначально предполагалось использовать этот класс в качестве базового, то такое решение очень спорное (на мой взгляд).
SB>Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия).
неужели увеличение производительности в этом случае будет (может быть) ощутимым?
я имею в виду, что конечно можно привести синтетический пример такого кода, но в реальных условиях, программист вряд ли бы принял решения о реализации такой иерархии. (это было личное мнение)
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Например, чтобы запретить удаление по указателю на базовый класс.
Это может быть полезно, например, при передаче указателя на базовый класс в качестве callback'a куда-нибудь. Чтобы в этом где-нибудь, могли вызывать методы интерфейса, но не могли удалять объект.
Чтобы выразить это явно, а не превращать увлекательную отладку странных глюков, деструктор нужно объявить как protected.
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Забыл
Не знал
Класс не предназначен для наследования. В этом случае в VMT нет смысла. В большинстве случаев не очень большой прирост в производительности (минус один jump) и по памяти (одно машинное слово на экземпляр класса), но может профилировщик сказал обратное?
Здравствуйте, Muxa, Вы писали:
SB>>Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия). M>неужели увеличение производительности в этом случае будет (может быть) ощутимым?
может
Не говоря уже о том, наличие вирт. деструктора автоматически переводит класс в категорию не-POD + non-trivial destructor.
M>я имею в виду, что конечно можно привести синтетический пример такого кода, но в реальных условиях, программист вряд ли бы принял решения о реализации такой иерархии. (это было личное мнение)
хз. Надо смотреть на иерархию. Это С++, а не Java, наследование тут используется по очень разным поводам.
T>Забыл T>Не знал
это сделано сознательно
T>Класс не предназначен для наследования. В этом случае в VMT нет смысла.
Класс предназначен для наследования.
T>В большинстве случаев не очень большой прирост в производительности (минус один jump) и по памяти (одно машинное слово на экземпляр класса), но может профилировщик сказал обратное?
про профилировщик не понял. система еще в разработке, появляются производные классы.
Здравствуйте, Muxa, Вы писали:
SB>>Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия). M>неужели увеличение производительности в этом случае будет (может быть) ощутимым? M>я имею в виду, что конечно можно привести синтетический пример такого кода, но в реальных условиях, программист вряд ли бы принял решения о реализации такой иерархии. (это было личное мнение)
Это может быть актуально, когда экземпляры класса создаются часто и в больших количествах (типа "умных указателей" или чего то подобного). Но, я так понимаю, здесь другой случай..
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Для того, чтобы избежать оверхед на виртуальный деструктор там, где не планируется полиморфное удаление.
Пример — boost::noncopyable
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Где-то читал (сам не проверял!) , что компиляторы умеют уложить экземпляр класса в один регистр (EAX) при возврате из функции по значению. При этом размер экземпляра, естественно, должен быть минимален. Если у экземепляра имеется таблица виртуальных методов, то такой фокус вряд ли пройдет.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
T>В большинстве случаев не очень большой прирост в производительности (минус один jump)
считать в инструкциях не очень осмысленно. Вызов невиртуального деструктора в большинстве случаев инлайнится, то есть никаких переходов, а тело функции оптимизируется непосредственно в контектсе вызова. Виртуальный -- это косвенный вызов со всеми прелестями сброса конвейера и отсутсвия оптимизации в контексте вызова.
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Реализовать гибкую стратегию владения объектами можно и без виртуальных деструкторов. Приведу пример. Я определяю некий абстракный интерфейс, состоящий полностью из чисто виртуальных функций:
Причем деструктор этого абстрактного класса я намеренно объявляю невиртуальным и защищенным, тем самым как бы подчеркивая, что стратегия владения объектами выбирается разработчиком производных классов. При этом у разработчика производного класса есть две возможности: 1) (традиционный) сделать деструктор производного класса открытым и виртуальным; 2) сделать деструктор невиртуальным и защищенным, а для создания объектов реализовать фабричный метод.
Пример второго варианта:
using boost::shared_ptr;
class FooDerived1 : public IFoo
{
public:
virtual void bar();
virtual void baz();
static shared_ptr<FooDerived1> create_instance(/*possible arguments*/);
{
return shared_ptr<FooDerived1>(new FooDerived(/*possible arguments*/));
}
protected:
FooDerived1();
};
class FooDerived2 : public IFoo { /*...*/ };
class FooDerived3 : public IFoo { /*...*/ };
А вот теперь замечательный момент: умный указатель производного типа, возвращаемый фабричным методом create_instance, легко преобразуется в умный указатель абстрактного типа. Это позволяет владеть объектами и корректно удалять их полиморфно:
Преимущество такого подхода: отделение основной функциональности абстрактных классов от способов владения конкретными объектами. Т.о. в прикладной программе точка создания объектов как бы изолирована от использования этих объектов. Это улучшает структурированность кода, тестируемость и пр. и др.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Muxa, Вы писали:
M>про профилировщик не понял. система еще в разработке, появляются производные классы.
Выделенное действие неясно. У вас архитектуру на ходу меняют что ли?
Да и вообще у тебя слишком сложный вопрос. Слишком общий. Опиши иерархию, что ли.
А то я легко могу придумать тебе пример класса, который базовый, но вообще без деструктора
Например так:
template<typename TAllocator> struct CAllocatedOn {
void* operator new( size_t s ) { return TAllocator::Alloc( s ); }
void operator delete( void* p ) { TAllocator::Free( p ); }
};
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
boost::shared_ptr<IFoo> p = items[0];
// Объект items удалён, p - единственный владелец указателя
p->bar();
// А теперь p должен быть уничтожен с удалением объекта класса IFoo
// Какой деструктор будет вызван?
Здравствуйте, andrey_nado, Вы писали:
_>Здравствуйте, rg45, Вы писали:
_>А что будет в следующем случае: _>
_>boost::shared_ptr<IFoo> p = items[0];
_>// Объект items удалён, p - единственный владелец указателя
p->>bar();
_>// А теперь p должен быть уничтожен с удалением объекта класса IFoo
_>// Какой деструктор будет вызван?
_>
Будет вызван деструктор производного класса, я пример приводил для иллюстрации именно этого факта. Объясняется это просто: в момент создания экземпляра boost::shared_ptr в нем запоминается не только указатель на объект, но также и функционал deleter, который обеспечивает корректное удаление адресуемых объектов, даже после преобразований.
Еще один аргумент. Деструктор абстрактного класса IFoo в моем примере объявлен защищенным, это является гарантией того, что извне класса этот деструктор не может быть вызван. Поэтому, если Ваша программа откомпилировалась без ошибок, на этот счет можете не волноваться
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Muxa, Вы писали:
M>Представьте себе иерархию классов. M>Разработчик самого базового из них не сделал деструктор виртуальным. M>Для чего он мог это сделать?
Чтобы спрашивать это на собеседовании и выявлять джуниоров.
SB>Это может быть актуально, когда экземпляры класса создаются часто и в больших количествах (типа "умных указателей" или чего то подобного). Но, я так понимаю, здесь другой случай..
Помоему при создании класса основные "трудозатраты" это работа менеджера памяти и выделение куска. А косвенный переход через виртуальную таблицу — это брызги для моряков по сравнению с выделением памяти и копированием.
Я программист, я Иван Помидоров, хватить трепатся — наш козырь error.