LINUX.ORG.RU

Как понять в базовом классе что виртуальная функция была перегружена в наследнике?

 


0

2
struct A{
	virtual void f(){}
	bool check(){ return ... } 
};
struct B: public A{
	void f(){}
};
struct C: public A{
};

Чего бы такого написать в A::check() что бы при вызове B().check() получать true а при вызове C().check() получать false?

Ввести в A флаг и изменять его значение неспортивно;-)

★★★★★

KennyMinigun, не, ну это почти как с флагом...

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

AntonI ★★★★★
() автор топика

Вот такого говна понапишут, а потом ноют, что ООП плохое.

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

О, спасибо. По второй ссылке работает, но кидает ворнинги при сборке. Хотя я не понял до конца почему this->*(&A::f) отличается от &A::f - если делать

void (A::*p)() = &A::f;
B b; (b.*p)();
то вызывается B::f().

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

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

Ты хочешь сравнить base.f и f?

Да.

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

Мдя... мы вроде кроме gcc ничего не планируем юзать, но кто его знает... может и интелом придется собирать;-(

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

Ок, спасибо.

Но я так и не понял чем this->*(&A::f) отличается от (&A::f).

A::f от f понятно чем отличается, но вроде как с т.з. указателя f и &A::f это одно и то же? Или это какая то gcc-шная магия?

AntonI ★★★★★
() автор топика

Никак. А ты думал, в сказку попал?

anonymous
()

Начать надо с того, нафига вообще может понадобиться такой код? Почему нельзя просто сделать check виртуальным или применить NVI?

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

Подобная задача в 99 случаях из 100 это элегантный способ стрелять в ногу.

peregrine ★★★★★
()

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

morse ★★★★★
()

Теги: яйцами в пилораму

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

А чем тут поможет NVI?

Может и ничем. Просто если нужно непременно иметь невиртуальный check, то он может вызывать уже виртуальный метод (какой-нибудь checkSpecial), из последнего уже можно добраться до run-time типа.

seiken ★★★★★
()

Эталонное «не делайте так» потому что если вам стало нужно это, у вас всё пошло очень сильно неправильно. Да, иногда такое может потребоваться но это крайне редкий случай и он точно не ваш

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

От телепата со стажем. ТСу нужен фактически RTTI но он этого не понимает, а значит что либо он ему не нужен (вероятность близка к 1) либо ему надо решать существенно более общую задачу, для решения которой ему надо очень сильно подтянуть теорию.

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

О, эти теплые ламповые телепаты лор-а со стажем, способные по двум комментариям оценить собеседника! Спасибо Настя, Вы сделали мой день.

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

Я не сразу сообразил, что ты хочешь сделать.

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

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

Это не решение проблем, это оптимизация. Все вопросы к идеологам NVI:-)

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

Это решается по другому. Создаётся класс А который работает по короткому пути и класс В который реализует длинный цикл и в котором обязательно переопределить функцию. Кому надо её переопределить наследуются от В, кому не надо от А. И собственно всё.

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

Да, я Вас не ошибся! Как любит говорить моя аспирантка - «безумие, больше безумия!»

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

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

тебе надо привилегию вызова цикла передать наследнику. для этого делаешь ещё один интерфейс и передаешь его в виртуальный метод. то что ты химичишь - аморально и безобразие. в следующий раз, когда заимплементируешь базовый класс, забудешь перегрузить check, и потом будешь с дебагером ебать мозг

struct base {
  struct cycleexecutor {
    void execute_cycle() = 0;
  }
  virtual void method(cycleexecutor &) {};
};

class ihneritee: base {
  void method(cycleexecutor &c) {c.execute_cycle();}
};

anonymous
()
Ответ на: комментарий от anonymous
struct cycleexecutor {
  struct Context {..};
  void execute_cycle(Context &) = 0;
};
  
struct base : cycleexecutor {
  virtual void method() {};
  void execute_cycle(Context &) {}
};

class ihneritee: base {
  void method() {Context c; execute_cycle(c);}
  void execute_cycle(Context &c) {for ...}
};

а ещё лучше так

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

Аморально и безобразие - лезть с советами по теме о которой не просили советовать.

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

сделать check виртуальным

Ну вон же хотеть проверять в рантайме!!!

нафига вообще может понадобиться такой код

А вот тут да.

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

Виртуальный чек лучше флага. Флаг надо в каждом экземпляре задавать, это придется городить конструктор.

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

Да уж, пришел неведомо кто, суть задачи рассказать не может, на все разумные замечания огрызается и обзывает желающих помочь телепатами…

Ну раз уж вы хотите извращений, то, пожалуйста.

class A {
private:
  static void default_func(A *) { ... };

protected:
  using func_ptr = void (*)(A*); // Или какой вам нужен набор аргументов.

  virtual func_ptr real_func() const noexcept { return &A::default_func; }

public:
  bool check() const noexcept { return &default_func == real_func(); }
  ...
};

class B : public A {
protected:
  static void custom_B_func(A *) { ... };

  func_ptr real_func() const noexcept override { return &B::custom_B_func; }
  ...
};

class C : public A {
... // Нет переопределения real_func.
};
eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от eao197

Отнюдь не всех, а только тех кто пытается играть в телепата и советовать по вопросаи о которых их не спрашивали. Что бы давать советы по дизайну надо знать специфику решаемой задачи, иначе такие советы разумными не будут. Я могу рассказать о специфике, но это будет много букв (и большой срач), и главное я для себя ничего ценного скорее всего не услышу. Зачем?

Теперь о Вашем варианте. Да, спасибо - это будет работать. Но виртуальный чек оказывается гораздо проще.

AntonI ★★★★★
() автор топика
Ответ на: комментарий от deep-purple

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

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

он нужен только для задания этих двух флагов

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

deep-purple ★★★★★
()
Ответ на: комментарий от AntonI

Я могу рассказать о специфике, но это будет много букв (и большой срач), и главное я для себя ничего ценного скорее всего не услышу. Зачем?

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

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

Если вы вот про такой вариант:

class A {
  virtual void f() {...}
  virtual bool check() const { return false; }
  ...
};
class B : public A {
  void f() override { ... }
  bool check() const override { return true; }
  ...
};

то он плох тем, что методы f() и check() никак не завязаны друг на друга и, по факту, являются независимыми. Поэтому это только вопрос времени когда у вас появится наследник, в котором f() переопределили, а check() – нет.

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

Нет, не больше. Перегрузить check - ровно одна строчка. Сделать конструктор - тоже одна строчка.

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

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

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

Поэтому это только вопрос времени когда у вас появится наследник, в котором f() переопределили, а check() – нет.

Для решаемой задачи (и НАШЕГО коллектива - я не говорю про вообще и всегда) это небольшая беда. Можно конечно сделать check полностью виртуальным, но пожалуй и это не нужно.

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

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

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

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

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

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

Никакой телепатии не нужно.

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

А ты пытаешься городить костыли.

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

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

Ладно, раз Вы просите. У базового класса (базовый солвер для FEM/XFEM) есть толстый сложный метод calc() который все делает. Для реализации конкретного солвера нужно отнаследовать базовый и перегрузить виртуальную ф-ю вычисляющую локальную матрицу жесткости для одного элемента сетки, дальше в calc() эта функция будет вызвана много раз, ее результаты будут обработаны (распиханы в глобальную матрицу), решена СЛАУ, вектор решений будет распихан по структуре данных. У наследника нет ни прав ни возможностей организовать цикл по элементам - он не видит всей структуры данных, не видит сетку, да они ему и ненужны, это все решает родитель.

Наследников будет штук пять, по числу решаемых систем уравнений. Для некоторых наследников, после сбора глобальной матрицы ее нужно пропатчить местами (поставить гранусловия). Это происходит где то в недрах calc. Идеология та же - есть вирт. фунцкия, которая возвращает правки для глобальной матрицы и правой части от каждого граничного узла/грани (элементов расчетной сетки). Наследник по прежнему не видит общей структуры данных, он не видит контекста calc, он не может орагнизовать цикл (иначе ему на вход придется передавать слишком много данных, и вообще это противоречит принципу разделяй и влавствуй). Можно вхолостую прогнать цикл (неперегруженная ф-я ничего не делает), но зачем? Это некрасиво.

Видите, столько букв - а толку никакого. Сейчас придут советники (телепатка Настя и анонимный школьник) и скажут что все сделано не так, а согласно теории надо этак. В итоге мы доберемся до основ мат.моделирования вообще и FEM/XFEM в частности, им будет непонятно а мне неинтересно. Все это уже проходилось тут много раз, в идеале мне просто надо наверное игнорировать такие замечания - но все из нас немного тролли;-)

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