LINUX.ORG.RU

Предупреждение при удалении элемента

 


0

1

Есть такой вот код:

...
std::map<int, UIElement*> elements;
...
void UIManager::deleteElement (UIElement *el) {

	elements[el->id] = nullptr;
	delete el;

}

Почему вылетает предупреждение, и опасно ли оно?

src/ui/manager.cpp:46:2: warning: delete called on 'UIElement' that has virtual
      functions but non-virtual destructor [-Wdelete-non-virtual-dtor]
        delete el;

★★★

Последнее исправление: Int64 (всего исправлений: 1)

КЕП подсказывает, что UIElement имеет виртуальные функции, но при этом не-виртуальный деструктор. Пацаны так не делают.

Pavval ★★★★★
()
Последнее исправление: Pavval (всего исправлений: 1)

У UIElement нет виртуального деструктора. Если ты удалишь UIElement по указателю на его базовый класс (или что вероятнее удалишь производный от UIElement класс по указателю на UIElement) может случиться что угодно.

KblCb ★★★★★
()

Кстати раз уж у тебя в тегах C++11, то правильнее будет использовать умные указатели. Как-то так:

...
std::map<int, std::unique_ptr<UIElement>> elements;
...
void UIManager::deleteElement (UIElement *el) {
	elements.erase(el->id);
}
archie
()
Ответ на: комментарий от KblCb

Если ты удалишь UIElement по указателю на его базовый класс может случиться что угодно.

В этом случае (сам класс, а не наследник) «что угодно» === «всё будет хорошо».

xaizek ★★★★★
()
Ответ на: комментарий от anonymous

Ни в каком, но в сообщении, на которое я отвечал, говорится:

Если ты удалишь UIElement по указателю на его базовый класс (или что вероятнее удалишь производный от UIElement класс по указателю на UIElement) может случиться что угодно.

Оно читается как «всё будет плохо даже если удалить UIElement по его же указателю», что неверно. И отсылка в базовому классу странная, как раз в самом базовом и надо править. Я просто смысл уточняю, а то ТС может не так понять в чём собственно проблема.

xaizek ★★★★★
()
Последнее исправление: xaizek (всего исправлений: 1)

Я всегда пользовался таким правилом: «Если есть хотябы один виртуальный метод, то обязательно должен быть виртуальный деструктор». Я даже не знаю могут ли быть исключения из этого правила. Если кто знает подскажите. Вот еще ссылка где автор ноет по поводу использования деструкторов (из серии могло быть и лучше):

http://www.gamasutra.com/blogs/PiotrTrochim/20150204/235736/Virtual_mess.php

CunMun
()
Ответ на: комментарий от CunMun

Я всегда пользовался таким правилом: «Если есть хотябы один виртуальный метод, то обязательно должен быть виртуальный деструктор». Я даже не знаю могут ли быть исключения из этого правила.

Наличие виртуального метода вовсе не обязательно влечет за собой наличие виртуального деструктора и наоборот. Эти вещи предназначены для решения разных задач.

Виртуальные методы определяют места изменения (или уточнения) поведения в классе-наследнике. Допустим, есть интерфейс desktop с виртуальными методами width(), height(), wallpaper() и т.д. Можно написать процедуру, которая будет получать ссылку/указатель на desktop и посредством методов desktop-а устанавливать на десктопе новую фоновую картину или отображать там какую-нибудь анимацию.

Но эта процедура не будет отвечать за то, как будет инстанцироваться конкретный экземпляр с интерфейсом desktop. Этот экземпляр может принадлежать классу windows_desktop или virtual_desktop, может создаваться не динамически, а быть автоматической переменной и т.д. Может оказаться что вообще в приложении не должно быть динамически созданных экземпляров desktop-а. Процедуре для смены фоновой картинки на это вообще наплевать. Поэтому эту процедуру можно будет применять как в обычном десктоп-приложении (где класс экземпляр класса windows_desktop будет создан один раз статически), так и в каком-нибудь веб-приложении (где экземпляр virtual_desktop может создаваться и связываться с http-сессией перед вызовом обработчика http-запроса). Пример взят с потолка, но для иллюстрации сойдет.

Более того, т.к. C++ — это язык с множественным наследованием, где можно смешать в одном классе кучу базовых классов, некоторые классы могут играть роль mixin-ов и не нуждаться в наличии деструкторов (тем более виртуальных). Тот же desktop может быть абстрактным классом, у которого все методы чистые виртуальные. А конкретный класс windows_desktop может наследоваться от класса canvas и использовать desktop в качестве mixin-а.

Виртуальный деструкторы нужны, когда в приложении есть необходимость управлять временем жизни динамически созданных полиморфных объектов. Таких как элементы UI, всякие Button, Label, ListBox, ComboBox и т.д., которые могут быть наследниками Widget-а. И приложению нужно динамически создавать экземпляры конкретных типов, но в дальнейшем работать с ними через указатель на базовый класс. Причем ключевой момент в том, что нужно удалять именно через указатель на базовый класс.

Во многих случаях виртуальные методы и виртуальные деструкторы не используются друг без друга. Но не всегда.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Спсибо вам за развернутый ответ. Цель виртуального деструктора как бы понятна. Пример из cppreferences:


class Base {
 public:
    virtual ~Base() {}
};
class Derived : public Base {};
Base* b = new Derived;
delete b; // safe

И компилятор слава богу предупреждает что если вам замаюдися удалить объект через ссылку на базовый клас то «undefined behaviour».

Если бы ктонибудь привел более конкретный пример того когда при наличии виртуального метода в базовом классе наличие виртуального деструктора скажем так противопоказана.

CunMun
()
Ответ на: комментарий от eao197

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

CunMun
()
Ответ на: комментарий от eao197

Читаешь такое и становится грустно.

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

Конечно, сделать как надо, можно только через жопу (темплейты).

Слава ООП!

Kuzy ★★★
()
Последнее исправление: Kuzy (всего исправлений: 1)
Ответ на: комментарий от Kuzy

Виртуальные методы нужны, только если нужна динамическая диспетчеризация.

Об этом было сказано: «Виртуальные методы определяют места изменения (или уточнения) поведения в классе-наследнике».

Во всех описанных тобой случаях, можно во время компиляции решить, какой метод вызвать.

Это только если при сборке все компоненты есть в исходных текстах. А если интерфейс desktop написан два года назад, метод set_wallpaper_on_desktop год назад, а класс windows_desktop полгода назад разными людьми, к нам приехали в виде заголовочных файлов и бинарных библиотек?

eao197 ★★★★★
()
Ответ на: комментарий от CunMun

По-хорошему, когда человек пишет в современном C++ деструктор, он должен понимать зачем и для чего это делается.

В C++11/14 уже достаточно средств для того, чтобы деструктор не нужно было писать вообще. Даже если классу нужно чистить какие-то ресурсы вручную, то для этого могут использоваться shared_ptr и unique_ptr, а так же другие обертки, реализующие идиому RAII.

Следовательно, если человек написал в базовом классе деструктор, то это должно что-то означать. Виртуальный деструктор явным образом показывает, что базовый класс дает возможность пользователю хранить и удалять экземпляры классов-потомков по указателю на базовый класс. Т.е. видя описание виртуального деструктора можно делать выводы о том, как мы себе можем позволить обращаться с объектами классов-потомков.

Если в базовом классе деструктора нет или есть, но не виртуальный, значит автор класса не дает нам возможность удалять потомков через указатель на базу. И тут нужно выяснять почему. Если код был написан новичком, это может быть и ошибка. А если опытным разработчиком, то может есть какие-то причины, которые не лежат на поверхности.

Что до отказа от виртуальности в угоду производительности, то такие вещи (в принципе как и все, связанное с производительностью), нужно делать только по объективным показаниям. Например, если на это показывает профайлер.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Если в базовом классе деструктора нет или есть, но не виртуальный, значит автор класса не дает нам возможность удалять потомков через указатель на базу. И тут нужно выяснять почему.

Да это как бы понятно. Врде разжовано уже много и везде. Я просто не могу предстваить себе такой юзкейс из серии виртуальные функции есть а виртуальный деструктор противопоказан.

Если в базовом классе деструктора нет или есть, но не виртуальный, значит автор класса не дает нам возможность удалять потомков через указатель на базу. И тут нужно выяснять почему.

Вот в этом я и пытаюсь разобратся почему? Зачем такое может понадобится..?

Я уже писал выше что как бы пользуюсь правилом (не помню где я это прочитал) «Если есть виртуальная функция то должен быть и виртуальный деструктор» ну хотябы на всякий случай )). И пока не могу найти опровержения этому правилу.

Я не говорю сейчас о том что с++ изменился существенно и можно спокойно говнокодить без new и delete и это даже пощряется.. Так оно и есть. Просто пытаюсь разобратся именно в этом вопросе. Я вон даже глянул на статью еще за царя гороха где Г. Саттер пытается ответить на эти вопрсы: http://www.gotw.ca/publications/mill18.htm И он там пишет что это был один из самых частых спорных вопросов на который сложно дать однозначный ответ.

Вы сильно не пинайте.. еслибы я был гуру в ваших плюсах, то такие бы вопросы не задавал.

CunMun
()
Ответ на: комментарий от CunMun

Вот в этом я и пытаюсь разобратся почему? Зачем такое может понадобится..?

Ну вот у меня недавно был как раз такой случай. База использовалась как сборище атрибутов, которые нужны всем наследникам. Наследники же с их помощью реализовывали свою собственную логику. Кроме того, изначально не требовалось, чтобы наследники были взаимозаменяемые, т.е. чтобы класс-наследник timer_wheel мог незаметно для пользователя заменить в приложении класс-наследник timer_list. Пользователь должен был создавать либо тот, либо другой явно, и не мог привести их к одному интерфейсу.

Ну или другой пример. Допустим, мы хотим создать аналоги классов std::vector, std::list и std::deque, но с mutex-ом внутри, чтобы операции push_back/pop_front можно было дергать с разных нитей. Можно ввести общий базовый класс synchronized_container, который внутри будет содержать экземпляр mutex-а + еще что-то. Наши классы vector, list и deque будут от synchronized_container наследоваться. Но пользователю нет никакого смысла держать у себя вектор указателей synchronized_container и удалять эти объекты посредством delete. Поэтому в synchronized_container виртуального деструктора не будет.

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

Ну и замечательное правило. Если его соблюдение позволяет спать спокойно, то нужно пользоваться.

А когда профайлер укажет, что виртуальные деструкторы жрут дефицитные такты процессора, тогда можно будет сделать рефакторинг.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Если в базовом классе деструктора нет или есть, но не виртуальный, значит автор класса не дает нам возможность удалять потомков через указатель на базу.

Если в базовом классе деструктора нет то тогда мы получаем:

Implicitly-declared destructor If no user-defined destructor is provided for a class type (struct, class, or union), the compiler will always declare a destructor as an inline public member of its class.

Если мы его задали явно но не виртуальным это нам вроде тоже ничего не дает, при наличии виртуального метода. Все что мы получим, как нам и говорит компилятор это undefined behavior при попытке удалить объект через указатель на базовый класс. Автор базового класса не имеет возможности запретить ссылатся через него на объекты наследники посредством указателя на этот же класс. Как я понимаю мы вообще не можем ничего запретить пользователю делать с сырыми указателями (ссылайся на что угодно, хоть на сами указатели, хоть вручную адрес вбивай) , тоесть в этом плане полная свобода действий.

CunMun
()

А тебя не смущает, что «elements[el->id] = nullptr;» не удаляет элемент из мапы, а просто заменяет его значение? Узел в дереве остаётся

Gvidon ★★★★
()
Ответ на: комментарий от CunMun

Все что мы получим, как нам и говорит компилятор это undefined behavior при попытке удалить объект через указатель на базовый класс.

Просто представьте себе, что могут быть случаи, когда вам дают какие-то объекты попользоваться, но возможности создать или удалить этот объект у вас нет.

Т.е. ваша задача может состоять в том, чтобы написать функцию:

void set_wallpaper_on_desktop(
  desktop & where,
  const picture & what );

Здесь desktop и picture могут быть интерфейсами с кучей виртуальных методов, но без виртуального деструктора. Потому, что автор инструмента, для которого вы пишете set_wallpaper_on_desktop, сам определяет политику создания и удаления таких объектов.

Автор базового класса не имеет возможности запретить ссылатся через него на объекты наследники посредством указателя на этот же класс.

Это и не нужно. Т.к. сделает невозможным написание вот такого кода:

windows_desktop real_desctop;
generated_on_demand_picture wallpaper;
...
set_wallpaper_on_desktop( real_desktop, wallpaper );

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

Ну так мы же в C++ находимся, не в Java какой-нибудь :)

Другое дело, что может быть библиотека, в которой базовый класс упрятан в какое-то внутреннее пространство имен, куда пользователь заглядывать не должен. А пользователю предоставляется для работы пачка готовых классов-наследников. Вот как у меня здесь: есть общая база details::thread_basic_t с виртуальным методом body, но без виртуального деструктора. Пользователь ничего не должен про нее знать. Он должен знать про классы, которые от нее были унаследованы, например, от timer_wheel_thread_template_t.

Но, т.к. это C++, ничего не мешает пользователю работать с details::thread_basic_t и удалять его наследников через указатель на thread_basic_t. При этом он сам будет нести ответственность за последствия.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Наши классы vector, list и deque будут от synchronized_container наследоваться. Но пользователю нет никакого смысла держать у себя вектор указателей synchronized_container и удалять эти объекты посредством delete. Поэтому в synchronized_container виртуального деструктора не будет.

Тогда зачем в synchronized_container виртуальные методы?

CunMun
()
Ответ на: комментарий от CunMun

«Если есть виртуальная функция то должен быть и виртуальный деструктор»

Ээээ... Для меня это странное правило. Может я в чем-то ошибаюсь, но я обычно исходил из правила, «если предполагается наследование, то деструктор должен быть виртуальным».

pathfinder ★★★★
()
Последнее исправление: pathfinder (всего исправлений: 2)
Ответ на: комментарий от CunMun

Тогда зачем в synchronized_container виртуальные методы?

Ну мало ли... Может методы lock/unlock будут объявлены виртуальными для того, чтобы производный класс мог их под себя как-то подтюнить.

eao197 ★★★★★
()
Ответ на: комментарий от pathfinder

Ээээ... Для меня это странное правило. Может я в чем-то ошибаюсь, но я обычно исходил из правила, «если предполагается наследование, то деструктор должен быть виртуальным».

наличие виртуальных функций как бы и предполагает возможное наследование. но в целом, чтобы тут не говорили, если класс учавствует или может учавствовать в иерархии наследования, то он _должен_ иметь виртуальный деструктор. а то сегодня мы вроде как гарантируем что удалять через указатель на базу не будем, а завтра и позабудем об этом и будем ловить веселые утечки ресурсов.

anonymous
()
Ответ на: комментарий от pathfinder

Ээээ... Для меня это странное правило. Может я в чем-то ошибаюсь, но я обычно исходил из правила, «если предполагается наследование, то деструктор должен быть виртуальным».

Если есть наследование и нет виртуальных методов то максимум что вы получите так это:

class Animal
{
public:
void eat() { std::cout << "I'm eating generic food."; }
}

class Cat : public Animal
{
public:
void eat() { std::cout << "I'm eating a rat."; }
}

void func(Animal *xyz) { xyz->eat(); }

int main()
{
Animal *animal = new Animal;
Cat *cat = new Cat;

func(animal); // outputs: "I'm eating generic food."
func(cat);    // outputs: "I'm eating generic food."

}

Что не так уж и страшно. И вполне очевидно (по сравнению с другими чудесами этого прекрасного языка). А обекты будут коректно уничтожены так как деструктор будет обявлен неявно.

CunMun
()
Ответ на: комментарий от pathfinder

«если предполагается наследование, то деструктор должен быть виртуальным»

Наследование может использоваться для разных целей. Может быть наследование интерфейса:

class desktop {
public :
  virtual int width() = 0;
  virtual int height() = 0;
  ...
};
...
class windows_desktop : public desktop {
public :
  virtual int width() override { ... }
  virtual int height() override { ... }
  ...
};

Может быть наследование реализации:

class synchronized_container {
public :
  locker lock() { return locker{ lock_; } }

  class locker { ... }

private :
  std::mutex lock_;
};
...
template< typename T >
class thread_safe_list : private synchronized_container {
public :
  void push_back( T&& what ) {
    auto l = lock();
    ...
  }
  void pop_front() {
    auto l = lock();
    ...
  }
  ...
};

При наследовании интерфейса, очень вероятно, виртуальный деструктор нужен. Хотя здесь могут быть нюансы с тем, дают ли пользователю интерфейса управлять временем жизни экземпляров или это не предусмотрено в принципе.

При наследовании реализации, напротив, смысла в виртуальном деструкторе не много. Т.к. такая база, скорее всего, может быть закрытой.

Хотя наследование реализации может быть заменено агрегацией. Но тут уж по месту нужно смотреть.

eao197 ★★★★★
()
Ответ на: комментарий от CunMun
int main()
{
Animal *animal = new Animal;
Cat *cat = new Cat;

func(animal); // outputs: "I'm eating generic food."
func(cat);    // outputs: "I'm eating generic food."

}

У вас здесь объекты не будут уничтожены вообще.

Вы с какого языка в C++ переходите?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Я не это хотел сказать.. То что нету delete понятно. Хотя и вверху я тоже ошибся. Наличие виртуального метода совсем не обязательно чтобы присутвовал виртальный деструктор. В любом случае если удаляем обект наследник с помощью указателья на базавый клас без виртуального деструктора получаем неопределенное поведение. P.S. Не знаю как здесь редактировать коментарии.

CunMun
()
Ответ на: комментарий от CunMun

Если есть наследование и нет виртуальных методов то максимум что вы получите так это

а как же что-то вроде этого?

class Terms: public std::vector<Term::Ptr> {
public:
   template <typename ... Types>
   explicit Terms(Types ... args): std::vector<Term::Ptr>(std::forward<Types>(args)...) {}

   template <typename T>
   Terms &operator<<(T p) { push_back(Term::create(p)); return *this; }
};
anonymous
()
Ответ на: комментарий от anonymous

Я имел в виду что максимум что вы получите в качестве отрицательного эфекта, но допустил ошибку, выше об этом написал.

CunMun
()
Ответ на: комментарий от CunMun

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

anonymous
()
Ответ на: комментарий от anonymous

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

Извините но вы меня наверное не правильно поняли. Я не говорил что наследование без виртуальных методов это вещь бесполезная. Я как раз задавал вопрос о том что к примеру если у нас есть класс именно с виртуальными методами то может ли быть в таком случае виртуальный деструктор противопоказан? Именно в контексте ошибки выдаваемой компилятором, о которой спрашивал ТС.

CunMun
()
Ответ на: комментарий от eao197

Виртуальные методы нужны, только если нужна динамическая диспетчеризация.

Об этом было сказано: «Виртуальные методы определяют места изменения (или уточнения) поведения в классе-наследнике».

марш читать «с++ для чайников». как закончишь, возвращайся со своими «советами».

то же мне эдик от крестов появился...

anonymous
()
Ответ на: комментарий от anonymous

марш читать «с++ для чайников». как закончишь, возвращайся со своими «советами».

Конкретное какое-то возражение можете привести?

Речь же идет о виртуальных методах, значит статический полиморфизм не рассматривается. Кроме динамической диспетчеризации для изменения поведения в наследнике ничего не остается.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

При наследовании реализации, напротив, смысла в виртуальном деструкторе не много. Т.к. такая база, скорее всего, может быть закрытой.

Согласен смысла в виртуальном деструкторе в данном случае не много. А в чем же тогда будет сакральный смысл в каком либо виртуальном методе в этом классе или даже в иеерархии?

Я как бы писал: «Если есть виртуальная функция то должен быть и виртуальный деструктор»

Предположим может ли быть необходимость спроэктировать базовый класс который будет содержать виртуальный метод (методы), но при этом виртуальный деструктор в нем бедет противопоказан (и по каким причинам)?

CunMun
()
Ответ на: комментарий от CunMun

«Противопоказан» — это неправильное слово.

Более правильное «избыточен». Могут быть случаи, когда виртуальный деструктор при наличии виртуальных методов может быть избыточен и достаточно будет обычного деструктора. Например, с тем же synchronized_container:

class synchronized_container {
public :
  virtual void lock() { l_.lock(); }
  virtual void unlock() { l_.unlock(); }

  class locker {
    synchronized_container * what_;
  public :
    locker( synchronized_container * what ) : what_(what) {
      what_->lock();
    }
    ~locker() {
      what_->unlock();
    }
  };

private :
  std::mutex l_;
};

template< class T >
class thread_safe_list : private synchronized_container {
public :
  void push_back(T&& o) {
    locker l{ this };
    ...
  }
  void pop_front() {
    locker l{ this };
    ...
  }
  ...
};

class my_ultra_fast_thread_safe_list : public thread_safe_list< some_my_type > {
...
private :
  virtual void lock() override { spin_.lock(); }
  virtual void unlock() override { spin_.unlock(); }

  super_puper_spinlock spin_;
};

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Я вас понял. Спасибо огромное за пример. Правда сразу воникают вопроссы: 1. Если мы таки добавляем виртуальный деструктор (хоть он и избыточный) мы получаем дополнительный overhead? Наверное да, как и любой виртуальный метод раздувает vtable. Хотя может я и ошибаюсь.

2. Тогда другой вопрос. Если мы заводим речь my_ultra_fast_thread_safe_list с намеком что все действительно должно быть ультра фаст то не стоит ли нам вообще старатся избегатть каких либо виртуальных методов?

CunMun
()
Ответ на: комментарий от eao197

А если интерфейс desktop написан два года назад, метод set_wallpaper_on_desktop год назад, а класс windows_desktop полгода назад разными людьми, к нам приехали в виде заголовочных файлов и бинарных библиотек?

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

Kuzy ★★★
()
Ответ на: комментарий от CunMun

1. Если мы таки добавляем виртуальный деструктор (хоть он и избыточный) мы получаем дополнительный overhead? Наверное да, как и любой виртуальный метод раздувает vtable. Хотя может я и ошибаюсь.

В принципе, получим. Но здесь нужно смотреть на то, как все используется. Современные компиляторы ну очень умные и в простых случаях от виртуальных вызовов вполне могут избавиться заинлайнив все действия. Так что на счет реальных накладных расходов нужно смотреть внимательно при помощи соответствующих инструментов.

2. Тогда другой вопрос. Если мы заводим речь my_ultra_fast_thread_safe_list с намеком что все действительно должно быть ультра фаст то не стоит ли нам вообще старатся избегатть каких либо виртуальных методов?

Виртуальный метод в C++ — это совсем не то, что виртуальный метод в каком-нибудь Ruby. Т.е. это настолько эффективная операция, что для того, чтобы реально выгадывать несколько тактов на прямом вызове, нужно иметь довольно специфические условия. Например, когда на объемное действие в прикладной логике отводятся буквально микросекунды.

Если не лень, то сделайте какой-нибудь примитивный microbenchmark, который покажет вам приблизительную стоимость виртуального вызова по сравнению с обычным. Думаю, тогда станет лучше понятно, что если в приложении идет активная работа с динамической памятью (т.е. постоянно дергаются new/delete), то стоимостью виртуальных вызовов вполне можно пренебречь.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Конкретное какое-то возражение можете привести?

рукалицо.jpg

Кроме динамической диспетчеризации для изменения поведения в наследнике ничего не остается.

перестань использовать термины, смысл которых не понимаешь.

anonymous
()
Ответ на: комментарий от anonymous

перестань использовать термины, смысл которых не понимаешь.

сказал с умным видом анонимный LOR-овский эксперт.

eao197 ★★★★★
()
Ответ на: комментарий от xaizek

В этом случае (сам класс, а не наследник) «что угодно» === «всё будет хорошо».

Наличие виртуальных функций кагбэ намекает на наследование и полиморфное использование, не?

Sectoid ★★★★★
()
Ответ на: комментарий от Sectoid

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

xaizek ★★★★★
()
Последнее исправление: xaizek (всего исправлений: 1)
Ответ на: комментарий от xaizek

Намекает, но не гарантирует.

Тогда скажи, для чего делать метод виртуальным, если класс не предполагает наследование?

Sectoid ★★★★★
()
Ответ на: комментарий от pashazz

в 90-х без него не обходилось ни одно собеседование по крестам. сейчас вроде считается само собой разумеющимся. но за пару последних лет я уже встречаю второго сектанта (см выше eao197), который выдумывает какой-то свой полиморфизм.

anonymous
()
Ответ на: комментарий от anonymous

хотя, его можно не считать. он в крестах вообще, и в полиморфизме в частности, разбирается как свинья в апельсинах.

anonymous
()
Ответ на: комментарий от Sectoid

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

xaizek ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.