Есть ли способ не наследовать "виртуальность" функции в подклассе? - PullRequest
9 голосов
/ 18 августа 2011

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

Причина, по которой я этого хочу, состоит в том, что у меня есть два класса, которые оба предоставляют функцию flush().В большинстве случаев в моей программе я вызываю flush() непосредственно для объекта подкласса, тип которого мне известен, поэтому мне не нужна виртуальная диспетчеризация.Однако я хочу добавить в класс суперкласс, чтобы очень редко я мог передать ссылку на экземпляр одного из классов в функцию doSomethingThenFlush(), которая фактически вызывала бы flush().

Iзнаю, что я мог бы использовать шаблоны вместо виртуальных функций, и я знаю, что у меня могут быть две разные функции (например, flushVirtual(), который просто вызывает flushNonVirtual(), и вызывать flushNonVirtual() везде, где мне не нужна виртуальная диспетчеризация).Но оба они выглядят как бросание кода в проблему, которая в значительной степени синтаксическая.Есть ли более изящный способ добиться этого?

Возможно, что еще важнее, кто-нибудь знает, почему виртуальность наследуется в C ++?

struct Base
{
  virtual ~Base(){}
  virtual void func();
};

struct Derived : public Base
{
  void func(){}
};

void callVirtually(Base &base)
{
  base.func();//this will use virtual dispatch
}

void callStatically(Derived &derived)
{
  derived.func();//I don't want/need this to use virtual dispatch
}

int main()
{
  Derived derived;
  callVirtually(derived);
  callStatically(derived);
}

Ответы [ 4 ]

8 голосов
/ 18 августа 2011

Виртуальность унаследована, потому что вы не знаете, будет ли кто-то дальше извлекать из вас Derived. Кто-то может с таким же успехом создать MoreDerived, который может быть передан функции, ожидающей Derived&, и ему будет грустно, если они обнаружат, что это были Derived версии всех ваших виртуальных функций, которые вместо этого вызывали bieng из MoreDerived х

Если вы имеете в виду, что никогда не будете наследовать от Derived, поэтому вы не хотите платить за вызов виртуальной функции, то вам не повезло, потому что C ++ не дает никакого способа обещать, что вы выиграли ' никогда не наследовать от класса, который был бы необходим, чтобы делать то, что вы хотите.

5 голосов
/ 18 августа 2011

В C ++ 03, нет.

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

Однако в C ++ 0x мы получаем два новых ключевых слова: override и final, и оба могут применяться к функциям-членам (final также может применяться к классу).

  • override: укажите, что эта функция переопределяет виртуальную функцию в базовом классе, полезно получать предупреждения, когда это не так
  • final: указать, что эта функция (виртуальная) не может быть переопределена в дочерних классах.

Таким образом, ваш класс станет:

struct Derived : public Base
{
  void func() final {}
};

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

4 голосов
/ 18 августа 2011

В вашем конкретном примере, если callStatically вставлено, компилятор, вероятно, избежит отправки виртуальной функции, потому что он может видеть фактический тип объекта (потому что это локальная переменная).

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

class Foo {
public:
    callStatically() { d.func() }
private:
    Derived d;
};

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

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

3 голосов
/ 18 августа 2011

Ответ лежит в вашем вопросе.

derived.func();  // no virtual dispatch

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

virtual функция появляется на картинке, только когда вы пытаетесь вызвать функцию, используя указатель или ссылка .

Редактировать : В своем обновленном вопросе вы используете Derived& для вызова func(). Звонок по ссылке обеспечит отправку virtual. Таким образом, нет языкового средства (например, final в Java), которое остановит отправку virtual.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...