Re: Зачем нужен невиртуальный деструктор?
От: jazzer Россия Skype: enerjazzer
Дата: 09.12.10 08:32
Оценка: 3 (2) +9
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Как вариант — не предназначал класс для полиморфного удаления.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: Зачем нужен невиртуальный деструктор?
От: jazzer Россия Skype: enerjazzer
Дата: 09.12.10 08:53
Оценка: 2 (1) +3
Здравствуйте, Muxa, Вы писали:

SB>>Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия).

M>неужели увеличение производительности в этом случае будет (может быть) ощутимым?
может
Не говоря уже о том, наличие вирт. деструктора автоматически переводит класс в категорию не-POD + non-trivial destructor.

M>я имею в виду, что конечно можно привести синтетический пример такого кода, но в реальных условиях, программист вряд ли бы принял решения о реализации такой иерархии. (это было личное мнение)

хз. Надо смотреть на иерархию. Это С++, а не Java, наследование тут используется по очень разным поводам.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: Зачем нужен невиртуальный деструктор?
От: kaa.python Ниоткуда РСДН профессионально мёртв и завален ватой.
Дата: 09.12.10 08:32
Оценка: :)))
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Чтоб те, кто будут работать с его иерархией, прочувствовали всю прелесть процесса отладки.
Re: Зачем нужен невиртуальный деструктор?
От: Suna Bozzu  
Дата: 09.12.10 08:35
Оценка: +3
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?
Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия).
Но, если изначально предполагалось использовать этот класс в качестве базового, то такое решение очень спорное (на мой взгляд).
Re[4]: Зачем нужен невиртуальный деструктор?
От: rg45 СССР  
Дата: 09.12.10 23:00
Оценка: 10 (1) +1
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Muxa, Вы писали:


M>>про профилировщик не понял. система еще в разработке, появляются производные классы.


E>Выделенное действие неясно. У вас архитектуру на ходу меняют что ли?

E>Да и вообще у тебя слишком сложный вопрос. Слишком общий. Опиши иерархию, что ли.
E>А то я легко могу придумать тебе пример класса, который базовый, но вообще без деструктора

E>Например так:
template<typename TAllocator> struct CAllocatedOn {
E>    void* operator new( size_t s ) { return TAllocator::Alloc( s ); }
E>    void operator delete( void* p ) { TAllocator::Free( p ); }
E>};


Как так, нет деструктора? Так не бывает. Пусть сгенерированный компилятором, но деструктор есть всегда. И в данном случае он тоже есть, его можно явно вызвать:
#include <iostream>
#include <cstdlib>

template<typename TAllocator> 
struct CAllocatedOn 
{
  void* operator new( size_t s ) { return TAllocator::Alloc( s ); }
  void operator delete( void* p ) { TAllocator::Free( p ); }
};

struct Allocator
{
  static void* Alloc(size_t s)  { return malloc(s); }
  static void Free(void* p)  { free(p); }
};

int main()
{
  typedef CAllocatedOn<Allocator> Foo;
  Foo foo;
  foo.~Foo();
}
--
Справедливость выше закона. А человечность выше справедливости.
Re: Зачем нужен невиртуальный деструктор?
От: datura-inoxia  
Дата: 09.12.10 08:49
Оценка: 2 (1) +1
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Например, чтобы запретить удаление по указателю на базовый класс.
Это может быть полезно, например, при передаче указателя на базовый класс в качестве callback'a куда-нибудь. Чтобы в этом где-нибудь, могли вызывать методы интерфейса, но не могли удалять объект.

Чтобы выразить это явно, а не превращать увлекательную отладку странных глюков, деструктор нужно объявить как protected.
Re[3]: Зачем нужен невиртуальный деструктор?
От: Suna Bozzu  
Дата: 09.12.10 08:58
Оценка: 2 (1) +1
Здравствуйте, Muxa, Вы писали:

SB>>Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия).

M>неужели увеличение производительности в этом случае будет (может быть) ощутимым?
M>я имею в виду, что конечно можно привести синтетический пример такого кода, но в реальных условиях, программист вряд ли бы принял решения о реализации такой иерархии. (это было личное мнение)
Это может быть актуально, когда экземпляры класса создаются часто и в больших количествах (типа "умных указателей" или чего то подобного). Но, я так понимаю, здесь другой случай..
Re: Зачем нужен невиртуальный деструктор?
От: rg45 СССР  
Дата: 09.12.10 09:55
Оценка: 1 (1) +1
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Реализовать гибкую стратегию владения объектами можно и без виртуальных деструкторов. Приведу пример. Я определяю некий абстракный интерфейс, состоящий полностью из чисто виртуальных функций:
class IFoo
{
public:

  virtual void bar() = 0;
  virtual void baz() = 0;

protected:
  IFoo() { }
};

Причем деструктор этого абстрактного класса я намеренно объявляю невиртуальным и защищенным, тем самым как бы подчеркивая, что стратегия владения объектами выбирается разработчиком производных классов. При этом у разработчика производного класса есть две возможности: 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, легко преобразуется в умный указатель абстрактного типа. Это позволяет владеть объектами и корректно удалять их полиморфно:
int main()
{
  typedef std::vector<boost::shared_ptr<IFoo> > Items;

  Items items;
  items.push_back(FooDerived1::create_instance(/*possible arguments*/));
  items.push_back(FooDerived2::create_instance(/*possible arguments*/));
  items.push_back(FooDerived3::create_instance(/*possible arguments*/));

  //...
}

Преимущество такого подхода: отделение основной функциональности абстрактных классов от способов владения конкретными объектами. Т.о. в прикладной программе точка создания объектов как бы изолирована от использования этих объектов. Это улучшает структурированность кода, тестируемость и пр. и др.
--
Справедливость выше закона. А человечность выше справедливости.
Re[3]: Зачем нужен невиртуальный деструктор?
От: Erop Россия  
Дата: 09.12.10 12:22
Оценка: 1 (1) -1
Здравствуйте, 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 ); }
};
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Зачем нужен невиртуальный деструктор?
От: slava_phirsov Россия  
Дата: 09.12.10 09:14
Оценка: +2
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Где-то читал (сам не проверял!) , что компиляторы умеют уложить экземпляр класса в один регистр (EAX) при возврате из функции по значению. При этом размер экземпляра, естественно, должен быть минимален. Если у экземепляра имеется таблица виртуальных методов, то такой фокус вряд ли пройдет.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[2]: Зачем нужен невиртуальный деструктор?
От: Muxa  
Дата: 09.12.10 19:52
Оценка: +1 :)
M>Чтобы спрашивать это на собеседовании и выявлять джуниоров.
сомневаюсь что задумка создателей языка была именно такая.
Re[5]: Зачем нужен невиртуальный деструктор?
От: Erop Россия  
Дата: 10.12.10 13:36
Оценка: +2
Здравствуйте, rg45, Вы писали:

E>>А то я легко могу придумать тебе пример класса, который базовый, но вообще без деструктора

R>Как так, нет деструктора? Так не бывает. Пусть сгенерированный компилятором, но деструктор есть всегда. И в данном случае он тоже есть, его можно явно вызвать:
Ну да. Прошу прощения, это просто жаргон. "без деструктора" надо читать, как "с тривиальным деструктором" конечно же. Это если точно и грамотно выражаться. Что на форуме для новичков С++ наверное правильно.

Спасибо за замечание.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Зачем нужен невиртуальный деструктор?
От: sergey_shandar США http://getboost.codeplex.com/
Дата: 11.02.11 11:19
Оценка: 4 (1)
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Muxa, Вы писали:


M>>Представьте себе иерархию классов.

M>>Разработчик самого базового из них не сделал деструктор виртуальным.
M>>Для чего он мог это сделать?

R>Реализовать гибкую стратегию владения объектами можно и без виртуальных деструкторов. Приведу пример. Я определяю некий абстракный интерфейс, состоящий полностью из чисто виртуальных функций:


Похожая стратегия используется в COM. Более того, при объявлении интрерфейсов (обычно они генерируются из IDL), виртуальный деструктор в интерфейсе не просто не нужен, а даже запрещен. Так как объявляя его вносится смещение в табицу виртуальных функций и интерфейс становится не совместимым c COM.
class IUnknown
{
public:
    virtual int AddRef();
    virtual int Release();
    virtual HRESULT QueryInterface(...параметры...);
};

class IA: public IUnknown
{
public:
    virtual HRESULT A();
};


При таком описании интерфейсов удобно использовать шаблон имплементирующий AddRef, Reelase для любого класса без всяких виртуальных деструкторов:
template<class Implementation>
class ComClass: Implementation
{
public:
    public int AddRef() override
    {
        ++counter;
        return counter;
    }
    public int Release() override
    {
        --counter;
        if(counter == 0)
        {
            delete this;
            return 0;
        }
    }
    static intrusive_ptr<Implementation> new_()
    {
        return intrusive_ptr<Implementation>(new ComClass());
    }
private:
    int counter;
    ComClass() {}
    ComClass(ComClass const &) {}
};

Пример:
// Абстрактный класс, AddRef, Release не реализованны (зачем их каждый раз реализовывать?). 
// Но реализованны функции интерфейса A.
class AImpl: IA
{
public:
    HRESULT A() override { ... }
};

ptr<AImpl> x = ComClass<AImpl>::new_();
x->A();
getboost.codeplex.com
citylizard.codeplex.com
com
Re: Зачем нужен невиртуальный деструктор?
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 13.12.10 19:41
Оценка: 2 (1)
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Не знаю, для чего он это сделал, но точно знаю почему такое есть у меня

Потому что экземпляры производных классов создаются исключительно в стеке.

Поэтому виртуальный деструктор — даром не нужен.
  Скрытый текст
/// <summary>
///  Интерфейс контекста операции с базой данных.
/// </summary>
//! Класс не предназначен для динамических объектов
class t_db_operation_context
{
 public:
  /// <summary>
  ///  Получение указателя на низкоуровневую транзакцию операции
  /// </summary>
  //! \return
  //!  Может быть NULL
  virtual t_db_transaction* get_transaction()=0;
  
  /// <summary>
  ///  Получение сервисного объекта
  /// </summary>
  //! \param[in] rguidService
  //! \return
  //!  NULL, если сервис с таким идентификатором не доступен
  virtual t_db_object_ptr get_service(REFGUID rguidService)=0;
};//class t_db_operation_context

Кстати, от него наследуется еще один абстрактный класс
  Скрытый текст
/// <summary>
///  Контекст операции с базой данных
/// </summary>
class t_ibp_operation_context:public db_obj::t_db_operation_context
{
 private:
  typedef t_ibp_operation_context              self_type;
  
  t_ibp_operation_context(const self_type&);
  self_type& operator = (const self_type&);
 
 public:
  t_ibp_operation_context(){;}
  
  //interface ------------------------------------------------------------
  
  /// <summary>
  ///  Получение ibp-транзакции в рамках которой выполняется операция
  /// </summary>
  //! \return
  //!  Can be NULL
  virtual t_ibp_transaction* ibp_transaction()=0;
  
  /// <summary>
  ///  Получение ibp-команды для которой выполняется операция
  /// </summary>
  //! \return
  //!  Can be NULL
  virtual t_ibp_command* ibp_command()=0;
};//class t_ibp_operation_context

А уже от него — собственно сама реализация
  Скрытый текст
/// <summary>
///  Реализация IBP-контекста для операций с базой данных
/// </summary>
//! 
//! \attention
//!  Методы интерфейса t_db_operation могут вызываться асинхронно.
class t_ibp_operation_context_std
 :public t_ibp_operation_context,
  public IBP_DEF_DB_INTERFACE_IMPL_STATIC(db_obj::t_db_operation)
{
 private:
  typedef t_ibp_operation_context_std       self_type;
  
  t_ibp_operation_context_std(const self_type&);
  self_type& operator = (const self_type&);
 
 public: //typedefs ------------------------------------------------------
  /// <summary>
  ///  Сигнальный тип, используемый для конструирования контекста для
  ///  операции получения метаданных
  /// </summary>
  struct for_meta_op{};
  
  class tag_attach_to_command;

 public:
  /// <summary>
  ///  Конструктор инициализации
  /// </summary>
  //! \param[in] transaction
  //! \param[in] command
  t_ibp_operation_context_std(t_ibp_transaction*  transaction,
                              t_ibp_command*      command);
  
  /// <summary>
  ///  Конструктор инициализации
  /// </summary>
  //! \param[in] transaction
  //! \param[in] command
  //! \param[in] pSession
  //!  Not NULL. Используется для создания временной транзакции
  //! \param[in] op_flags
  t_ibp_operation_context_std(t_ibp_transaction*  transaction,
                              t_ibp_command*      command,
                              TIBPSession*        pSession,
                              TIBP_OperationFlags op_flags);
  
  /// <summary>
  ///  Конструктор инициализации контекста для получения метаданных
  /// </summary>
  //! \param[in] tag
  //! \param[in] transaction
  //! \param[in] pSession
  t_ibp_operation_context_std(for_meta_op         tag,
                              t_ibp_transaction*  transaction,
                              TIBPSession*        pSession);
  /// <summary>
  ///  Деструктор
  /// </summary>
 ~t_ibp_operation_context_std();
  
  //interface ------------------------------------------------------------
  
  /// <summary>
  ///  Завершение работы. Выполняем коммит автоматической транзакции.
  /// </summary>
  void complete();

  /// <summary>
  ///  Установка новой команды
  /// </summary>
  //! \param[in] command
  //!  Can be NULL
  //!
  //! \attention
  //!  Если command!=NULL, то контекст не должен быть привязан к другой команде.
  void set_ibp_command(t_ibp_command* command);

  //t_db_operation interface ---------------------------------------------

  /// <summary>
  ///  Отмена операции. Вызывается асинхронно.
  /// </summary>
  virtual void cancel();//abstract

  //t_db_operation_context interface -------------------------------------
  
  /// <summary>
  ///  Получение указателя на низкоуровневую транзакцию операции
  /// </summary>
  //! \return
  //!  Может быть NULL
  virtual t_db_transaction* get_transaction();//abstract
  
  /// <summary>
  ///  Запрос сервисного объекта
  /// </summary>
  //! \param[in] rguidService
  //!  Идентификатор сервиса
  //! \return
  //!  NULL, если сервис с запрошенным идентификатором не доступен.
  virtual t_db_object_ptr get_service(REFGUID rguidService);//abstract

  //t_ibp_operation_context interface ------------------------------------
  
  /// <summary>
  ///  Получение ibp-транзакции в рамках которой выполняется операция
  /// </summary>
  //! \return
  //!  Can be NULL
  virtual t_ibp_transaction* ibp_transaction();
  
  /// <summary>
  ///  Получение ibp-команды для которой выполняется операция
  /// </summary>
  //! \return
  //!  Can be NULL
  virtual t_ibp_command* ibp_command();

 private: //typedefs -----------------------------------------------------
  typedef t_db_service_descr                                   svc_descr_type;
  typedef t_db_service_descr::tag_less                         svc_descr_less_type;
  
  typedef t_db_memory_allocator                                allocator_type;
  
  typedef t_tree_avl<svc_descr_type,
                     svc_descr_less_type,
                     allocator_type>                           services_type;
  
 private:
  typedef structure::t_multi_thread_traits                     thread_traits;
  typedef thread_traits::guard_type                            guard_type;
  typedef thread_traits::lock_guard_type                       lock_guard_type;

 private:
  //! \brief Указатель на связанную ibp-транзакцию
  t_ibp_transaction_ptr const m_transaction;
  
  //! \brief Указатель на связанную ibp-команду
  t_ibp_command_ptr          m_command;
 
  TIBPSessionCPtr      const m_cspSession;
  
  TIBP_OperationFlags  const m_op_flags;

 private:
  //! \brief Кэш сервисов.
  services_type m_services;

 private:
  //! \brief Блокировщик временной транзакции
  t_ibp_auto_transaction_locker m_TmpTrans;

 private:
  //! \brief Защита указателя на текущую операцию
  guard_type m_cur_op_guard;

  //! \brief Сигнал отмены операции
  thread_traits::int_type m_cancel_flag;

  //! \brief Текущая операция
  //! \todo  Заменить на t_db_operation_ptr
  t_ibp_command_ptr m_cur_op;
};//class t_ibp_operation_context_std

Типа вот.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: Зачем нужен невиртуальный деструктор?
От: rg45 СССР  
Дата: 14.02.11 04:40
Оценка: 1 (1)
Здравствуйте, bzzz, Вы писали:

R>>...


B>Всё классно но ваш пример не работает, не могли бы вы написать работающий пример?


Да, конечно, приведенные мной фрагменты кода носят чисто описательный характер и не претендуют на статус работающего примера.

А вот работающий пример.

//IFoo.hpp
class IFoo
{
public:

  virtual void bar() = 0;

protected:
  IFoo() { }
};

//*.cpp
//#include "IFoo.hpp"
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <vector>

using boost::shared_ptr;

#define PRINT_FUNCTION_NAME() { std::cout << __FUNCTION__ << std::endl; }

class FooDerived1 : public IFoo
{
public:

  virtual void bar() { PRINT_FUNCTION_NAME(); }
  
  static shared_ptr<FooDerived1> create_instance()
  {
    return shared_ptr<FooDerived1>(new FooDerived1(), destroy);
  }

protected:
  ~FooDerived1() { PRINT_FUNCTION_NAME(); }
  static void destroy(FooDerived1* inst) { delete inst; }
};

class FooDerived2 : public IFoo 
{ 
public:

  virtual void bar() { PRINT_FUNCTION_NAME(); }

  virtual ~FooDerived2() { PRINT_FUNCTION_NAME(); }
};

void use(const std::vector<shared_ptr<IFoo> >& items)
{
  typedef std::vector<shared_ptr<IFoo> > Items;
  for(Items::const_iterator item = items.begin(); item != items.end(); ++item)
    (*item)->bar();
}

void do_nothing(void*) { }

int main()
{
  typedef std::vector<boost::shared_ptr<IFoo> > Items;

  Items items;
  
  FooDerived2 local;
  items.push_back(shared_ptr<FooDerived2>(&local, do_nothing));
  items.push_back(FooDerived1::create_instance());
  items.push_back(shared_ptr<FooDerived2>(new FooDerived2()));

  use(items);
}


Output:

FooDerived2::bar
FooDerived1::bar
FooDerived2::bar
FooDerived2::~FooDerived2
FooDerived1::~FooDerived1
FooDerived2::~FooDerived2


Собрано на msvc-9.0 (MSVS 2008), boost 1.45.0.

А теперь немного повторюсь. Ценность этого подхода в том, мухи отделены от котлет. Те части программы, которые используют полиморфные объекты, не интересуются ни способом их создания, ни областью памяти, в которой хранятся эти объекты, ни временем их жизни. Так, в этом примере два объекта расположены в динамической памяти, а один — во временной памяти функции main. Но внутри функции use, использующей эти объекты, все эти детали не интересны. А защищенный деструктор в абстрактном базовом классе дает нам защищенность от очумелых ручек, способных написать что угодно, в т.ч. и такое:

shared_ptr<IFoo> shared = ...;
IFoo* ptr = shared.get();
//...
delete ptr;
//...
--
Справедливость выше закона. А человечность выше справедливости.
Re: Зачем нужен невиртуальный деструктор?
От: telek1024  
Дата: 09.12.10 08:50
Оценка: -1
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

  1. Забыл
  2. Не знал
  3. Класс не предназначен для наследования. В этом случае в VMT нет смысла. В большинстве случаев не очень большой прирост в производительности (минус один jump) и по памяти (одно машинное слово на экземпляр класса), но может профилировщик сказал обратное?
Re: Зачем нужен невиртуальный деструктор?
От: Alexander G Украина  
Дата: 09.12.10 09:00
Оценка: +1
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Для того, чтобы избежать оверхед на виртуальный деструктор там, где не планируется полиморфное удаление.
Пример — boost::noncopyable
Русский военный корабль идёт ко дну!
Re: Зачем нужен невиртуальный деструктор?
От: minorlogic Украина  
Дата: 09.12.10 19:32
Оценка: -1
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Чтобы спрашивать это на собеседовании и выявлять джуниоров.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[5]: Не единым аллокатором жив С++, в отличии от... ;)
От: Erop Россия  
Дата: 10.12.10 13:50
Оценка: +1
Здравствуйте, ankf, Вы писали:

A>Помоему при создании класса основные "трудозатраты" это работа менеджера памяти и выделение куска. А косвенный переход через виртуальную таблицу — это брызги для моряков по сравнению с выделением памяти и копированием.


Бывают массивы объектов, бывают пулы блоков памяти, бывают автоматические объекты, наконец
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: немного оффтопа
От: uzhas Ниоткуда  
Дата: 10.12.10 14:38
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>и где в стандарте то место, по которому следует что так можно:

R>а так нельзя:

аналогичный код
int x = int(); // OK
int* y = int*(); // FAIL

typedef int* intPtr;
int* z = intPtr(); // OK
Re[3]: Зачем нужен невиртуальный деструктор?
От: npak Россия  
Дата: 11.12.10 08:09
Оценка: +1
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, npak, Вы писали:


M>>>Представьте себе иерархию классов.


N>>Может быть, предполагается совместимость с С? Если нет виртуальных функций, то раскладка полей в памяти будет как в С. Если есть виртуальные функции — то "внутри" объекта появятся указатели на таблицы виртуальных функций (у наследников таких указателей может быть больше, чем 1)


E>А как иерархия может быть совместима с С?


Вот выхолощенный пример, такой прием используется при описаниях протоколов на С

struct BaseHeader {
    int f1;
    int f2;
};

struct ExtendedHeader {
    struct BaseHeader base;
    int f3;
};

struct Message {
    struct ExtendedHeader base;
    int f4;
};


Если написать иерархию на С++
struct BaseHeader {
    int f1;
    int f2;
};

struct ExtendedHeader : public BaseHeader {
    int f3;
};

struct Message : public ExtendedHeader {
    int f4;
};


то типы этой иерархии будут разложены в памяти (memory layout) так же, как аналогичные типы из С. То есть сишная и плюсовая программа/функции смогут обмениваться бинарными данными. Но для этого необходимо, чтобы в С++ не было виртуальных функций, иначе раскладка полей в памяти будет перемешана с указателями на таблицы виртуальных функций.
Зачем нужен невиртуальный деструктор?
От: Muxa  
Дата: 09.12.10 08:24
Оценка:
Представьте себе иерархию классов.
Разработчик самого базового из них не сделал деструктор виртуальным.
Для чего он мог это сделать?
Re[2]: Зачем нужен невиртуальный деструктор?
От: Muxa  
Дата: 09.12.10 08:42
Оценка:
SB>Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия).
неужели увеличение производительности в этом случае будет (может быть) ощутимым?
я имею в виду, что конечно можно привести синтетический пример такого кода, но в реальных условиях, программист вряд ли бы принял решения о реализации такой иерархии. (это было личное мнение)
Re[2]: Зачем нужен невиртуальный деструктор?
От: Muxa  
Дата: 09.12.10 08:54
Оценка:
T>Забыл
T>Не знал
это сделано сознательно

T>Класс не предназначен для наследования. В этом случае в VMT нет смысла.

Класс предназначен для наследования.

T>В большинстве случаев не очень большой прирост в производительности (минус один jump) и по памяти (одно машинное слово на экземпляр класса), но может профилировщик сказал обратное?

про профилировщик не понял. система еще в разработке, появляются производные классы.
Re[2]: Зачем нужен невиртуальный деструктор?
От: dilmah США  
Дата: 09.12.10 09:14
Оценка:
T>В большинстве случаев не очень большой прирост в производительности (минус один jump)

считать в инструкциях не очень осмысленно. Вызов невиртуального деструктора в большинстве случаев инлайнится, то есть никаких переходов, а тело функции оптимизируется непосредственно в контектсе вызова. Виртуальный -- это косвенный вызов со всеми прелестями сброса конвейера и отсутсвия оптимизации в контексте вызова.
Re[2]: Зачем нужен невиртуальный деструктор?
От: andrey_nado  
Дата: 09.12.10 14:44
Оценка:
Здравствуйте, rg45, Вы писали:

А что будет в следующем случае:
boost::shared_ptr<IFoo> p = items[0];
// Объект items удалён, p - единственный владелец указателя
p->bar();
// А теперь p должен быть уничтожен с удалением объекта класса IFoo
// Какой деструктор будет вызван?
Re[3]: Зачем нужен невиртуальный деструктор?
От: rg45 СССР  
Дата: 09.12.10 15:05
Оценка:
Здравствуйте, andrey_nado, Вы писали:

_>Здравствуйте, rg45, Вы писали:


_>А что будет в следующем случае:

_>
_>boost::shared_ptr<IFoo> p = items[0];
_>// Объект items удалён, p - единственный владелец указателя
p->>bar();
_>// А теперь p должен быть уничтожен с удалением объекта класса IFoo
_>// Какой деструктор будет вызван?
_>


Будет вызван деструктор производного класса, я пример приводил для иллюстрации именно этого факта. Объясняется это просто: в момент создания экземпляра boost::shared_ptr в нем запоминается не только указатель на объект, но также и функционал deleter, который обеспечивает корректное удаление адресуемых объектов, даже после преобразований.

Еще один аргумент. Деструктор абстрактного класса IFoo в моем примере объявлен защищенным, это является гарантией того, что извне класса этот деструктор не может быть вызван. Поэтому, если Ваша программа откомпилировалась без ошибок, на этот счет можете не волноваться
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: Зачем нужен невиртуальный деструктор?
От: ankf  
Дата: 09.12.10 19:37
Оценка:
SB>Это может быть актуально, когда экземпляры класса создаются часто и в больших количествах (типа "умных указателей" или чего то подобного). Но, я так понимаю, здесь другой случай..

Помоему при создании класса основные "трудозатраты" это работа менеджера памяти и выделение куска. А косвенный переход через виртуальную таблицу — это брызги для моряков по сравнению с выделением памяти и копированием.
Я программист, я Иван Помидоров, хватить трепатся — наш козырь error.
Re[3]: Зачем нужен невиртуальный деструктор?
От: minorlogic Украина  
Дата: 09.12.10 19:57
Оценка:
Здравствуйте, Muxa, Вы писали:

M>>Чтобы спрашивать это на собеседовании и выявлять джуниоров.

M>сомневаюсь что задумка создателей языка была именно такая.

задумка тут не причем , я попытался представить как такой вопрос может возникнуть.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[4]: Зачем нужен невиртуальный деструктор?
От: Muxa  
Дата: 09.12.10 21:03
Оценка:
M>задумка тут не причем , я попытался представить как такой вопрос может возникнуть.
у человека, который не программировал на плюсах, еще и не такие вопросы могут возникнуть
Re[5]: немного оффтопа
От: rg45 СССР  
Дата: 10.12.10 08:55
Оценка:
Здравствуйте, rg45, Вы писали:

R>Как так, нет деструктора? Так не бывает...


То, что у всех типов, даже у скалярных, формально есть деструктор можно показать так:
template<typename T>
void foo(T t)
{
  t.~T();
}

int main()
{
  foo(42);
}

Но вот что интересно, можно сделать так:
typedef int Foo;
int i = 42;
i.~Foo(); //Ok

но почему-то нельзя сделать так:
int i = 42;
i.~int(); //error: expected an identifier

--
Справедливость выше закона. А человечность выше справедливости.
Re[6]: немного оффтопа
От: Erop Россия  
Дата: 10.12.10 13:54
Оценка:
Здравствуйте, rg45, Вы писали:

R>То, что у всех типов, даже у скалярных, формально есть деструктор можно показать так:

R>

На самом деле это не так. Просто доля параметров шаблонов и псевдонимов имён есть специальный синтаксис, похожий на вызовы конструкторов и деструкторов.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: немного оффтопа
От: rg45 СССР  
Дата: 10.12.10 14:27
Оценка:
Здравствуйте, Erop, Вы писали:

E>На самом деле это не так. Просто доля параметров шаблонов и псевдонимов имён есть специальный синтаксис, похожий на вызовы конструкторов и деструкторов.


Ну, что касается шаблонов, это я еще понимаю. Но в связи с чем к псевдонимам типов применяются правила, отличные от правил, применяемым к оригинальным именам? Чем обосновано, и где в стандарте то место, по которому следует что так можно:
typedef int Foo;
int i = 42;
i.~Foo();

а так нельзя:
int i = 42;
i.~int();

--
Справедливость выше закона. А человечность выше справедливости.
Re[8]: немного оффтопа
От: Erop Россия  
Дата: 10.12.10 15:03
Оценка:
Здравствуйте, rg45, Вы писали:

R>по которому следует что так можно:

R>
R>typedef int Foo;
R>int i = 42;
R>i.~Foo();
R>

Ну я ссылку тебе не дам, но там есть ровно про этот случай и про аргумент шаблона тоже. Что типа даже для строенного типа так писать можно, и это будет обозначать "ничего не делай"


R>а так нельзя:

R>
R>int i = 42;
R>i.~int();
R>

R>

А про нельзя нет. Просто такой конструкции нет в стандарте.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Зачем нужен невиртуальный деструктор?
От: npak Россия  
Дата: 11.12.10 00:10
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?

Может быть, предполагается совместимость с С? Если нет виртуальных функций, то раскладка полей в памяти будет как в С. Если есть виртуальные функции — то "внутри" объекта появятся указатели на таблицы виртуальных функций (у наследников таких указателей может быть больше, чем 1)
Re[2]: Зачем нужен невиртуальный деструктор?
От: Erop Россия  
Дата: 11.12.10 00:38
Оценка:
Здравствуйте, npak, Вы писали:

M>>Представьте себе иерархию классов.


N>Может быть, предполагается совместимость с С? Если нет виртуальных функций, то раскладка полей в памяти будет как в С. Если есть виртуальные функции — то "внутри" объекта появятся указатели на таблицы виртуальных функций (у наследников таких указателей может быть больше, чем 1)


А как иерархия может быть совместима с С?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Зачем нужен невиртуальный деструктор?
От: vdimas Россия  
Дата: 11.12.10 08:31
Оценка:
Здравствуйте, Suna Bozzu, Вы писали:

SB>Возможно, для того, что бы не создавалась таблица виртуальных функций (для увеличения быстродействия).

SB>Но, если изначально предполагалось использовать этот класс в качестве базового, то такое решение очень спорное (на мой взгляд).

Ничего страшного, можно обезопаситься через объявление деструктора как protected.
Re[2]: Зачем нужен невиртуальный деструктор?
От: 24  
Дата: 13.12.10 19:49
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Потому что экземпляры производных классов создаются исключительно в стеке.


Виртуальный деструктор в этом случае привнесёт какие-то недостатки?
Re[3]: Зачем нужен невиртуальный деструктор?
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 13.12.10 20:09
Оценка:
Здравствуйте, 24, Вы писали:

КД>>Потому что экземпляры производных классов создаются исключительно в стеке.


24>Виртуальный деструктор в этом случае привнесёт какие-то недостатки?


Да нет, вроде. Я так полагаю, компилятор (когда будет вызывать ~t_ibp_operation_context_std) вообще к VT обращаться не будет — тип объекта же и так ясен.

Но определять вируальный деструктор в базовом классе — все равно не тянет

Вот если бы это был базовый класс для динамических (или как там они называются) объектов, то виртуальный деструктор появился бы. Кстати ... а нету его там нифига — он появляется в производном классе, который реализует метод release с его delete this. И ничего — живем!
  Скрытый текст
/// <summary>
///  Base class for smart interface.
/// </summary>
template<class tag_thread_traits>
class COMP_CONF_DECLSPEC_NOVTABLE t_basic_smart_interface
{
 public: //typedefs ------------------------------------------------------
  typedef tag_thread_traits                           thread_traits;
  typedef typename thread_traits::int_type            cnt_ref_type;

 public:
  virtual cnt_ref_type add_ref()=0;
  virtual cnt_ref_type release()=0;
};//class t_basic_smart_interface
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[4]: Зачем нужен невиртуальный деструктор?
От: TimurSPB Интернет  
Дата: 13.12.10 20:26
Оценка:
N>то типы этой иерархии будут разложены в памяти (memory layout) так же, как аналогичные типы из С. То есть сишная и плюсовая программа/функции смогут обмениваться бинарными данными. Но для этого необходимо, чтобы в С++ не было виртуальных функций, иначе раскладка полей в памяти будет перемешана с указателями на таблицы виртуальных функций.

В реальном продукте я был бы против кода, основанного на таких предположениях.
Make flame.politics Great Again!
Re[2]: Зачем нужен невиртуальный деструктор?
От: programmater  
Дата: 14.12.10 16:47
Оценка:
Здравствуйте, datura-inoxia, Вы писали:

DI>Здравствуйте, Muxa, Вы писали:


M>>Представьте себе иерархию классов.

M>>Разработчик самого базового из них не сделал деструктор виртуальным.
M>>Для чего он мог это сделать?

DI>Например, чтобы запретить удаление по указателю на базовый класс.

DI>Это может быть полезно, например, при передаче указателя на базовый класс в качестве callback'a куда-нибудь. Чтобы в этом где-нибудь, могли вызывать методы интерфейса, но не могли удалять объект.

тут вроде речь идет о невиртуальном деструкторе. А для того, о чем ты пишешь, деструктор должен быть protected (и ты сам в конце об этом же говоришь).
DI>Чтобы выразить это явно, а не превращать увлекательную отладку странных глюков, деструктор нужно объявить как protected.А вот какое отношение имеет одно (невиртуальность) к другому (протектнутости) я честно говоря не уловил.

ЗЫ. сам довольно часто использую невиртуальные public деструкторы. Т.к. в большинстве случаев объект (базового класса или производного — без разницы) является автоматическим (стековым) объектом. И только если предполагается удалять объект по указателю на базовый класс — вот только тогда делаю деструктор базового класса виртуальным. Но прежде чем так сделать сперва хорошенько подумаю, а нельзя ли решить задачу используя автоматические объекты. И только если уж совсем никак и без удаления по указателю на базовый класс не обойтись — ну тогда виртуальный деструктор в руки .
Re[2]: Зачем нужен невиртуальный деструктор?
От: bzzz  
Дата: 10.02.11 15:24
Оценка:
Здравствуйте, rg45, Вы писали:

R>
R>class IFoo
R>{
R>public:

R>  virtual void bar() = 0;
R>  virtual void baz() = 0;

R>protected:
R>  IFoo() { }
R>};
R>

R>Причем деструктор этого абстрактного класса я намеренно объявляю невиртуальным и защищенным, тем самым как бы подчеркивая, что стратегия владения объектами выбирается разработчиком производных классов. При этом у разработчика производного класса есть две возможности: 1) (традиционный) сделать деструктор производного класса открытым и виртуальным; 2) сделать деструктор невиртуальным и защищенным, а для создания объектов реализовать фабричный метод.

R>Пример второго варианта:

R>
R>using boost::shared_ptr;

R>class FooDerived1 : public IFoo
R>{
R>public:

R>  virtual void bar();
R>  virtual void baz();

R>  static shared_ptr<FooDerived1> create_instance(/*possible arguments*/);
R>  {
R>    return shared_ptr<FooDerived1>(new FooDerived(/*possible arguments*/));
R>  }

R>protected:
R>  FooDerived1();
R>};

R>class FooDerived2 : public IFoo { /*...*/ };
R>class FooDerived3 : public IFoo { /*...*/ };
R>

R>А вот теперь замечательный момент: умный указатель производного типа, возвращаемый фабричным методом create_instance, легко преобразуется в умный указатель абстрактного типа. Это позволяет владеть объектами и корректно удалять их полиморфно:
R>
R>int main()
R>{
R>  typedef std::vector<boost::shared_ptr<IFoo> > Items;

R>  Items items;
R>  items.push_back(FooDerived1::create_instance(/*possible arguments*/));
R>  items.push_back(FooDerived2::create_instance(/*possible arguments*/));
R>  items.push_back(FooDerived3::create_instance(/*possible arguments*/));

R>  //...
R>}
R>


Всё классно но ваш пример не работает, не могли бы вы написать работающий пример?
Re: Зачем нужен невиртуальный деструктор?
От: Vain Россия google.ru
Дата: 10.02.11 18:24
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Представьте себе иерархию классов.

M>Разработчик самого базового из них не сделал деструктор виртуальным.
M>Для чего он мог это сделать?
Ещё как вариант (кроме только стекового использования) заставить пользователя пользоваться правильными смартпоинтерами — boost::shared_ptr, а ему виртуальный деструктор не нужен.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[4]: Зачем нужен невиртуальный деструктор?
От: bzzz  
Дата: 14.02.11 16:15
Оценка:
Большое спасибо за разъяснение)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.