Переопределение функции C ++

У меня есть три разных базовых класса:

class BaseA
{
public:
    virtual int foo() = 0;
};

class BaseB
{
public:
    virtual int foo() { return 42; }
};

class BaseC
{
public:
    int foo() { return 42; }
};

Затем я получаю от базы следующим образом (заменяю X на A, B или C):

class Child : public BaseX
{
public:
    int foo() { return 42; }
};

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

  • С BaseA дочерний класс не компилируется, чистая виртуальная функция не определяется.
  • В BaseB функция в дочернем элементе вызывается при вызове foo для BaseB * или Child *.
  • В BaseC функция в дочернем классе вызывается при вызове foo в Child *, но не в BaseB * (вызывается функция в родительском классе).

person Mizipzor    schedule 22.06.2009    source источник


Ответы (6)


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

  • С BaseA он будет компилироваться и выполняться, как задумано, при этом foo() является виртуальным и выполняется в классе Child.
  • То же самое с BaseB, он также будет компилироваться и выполняться по назначению, при этом foo() является virtual () и выполняется в классе Child.
  • Однако с BaseC он будет компилироваться и выполняться, но он выполнит версию BaseC, если вы вызовете ее из контекста BaseC, и версию Child, если вы вызовете с контекстом Child.
person Jared Oberhaus    schedule 22.06.2009

Важно помнить, что после объявления функции виртуальной функции с соответствующими сигнатурами в производных классах всегда становятся виртуальными. Таким образом, он переопределен для Child of A и Child of B, которые будут вести себя одинаково (за исключением того, что вы не можете напрямую создать экземпляр BaseA).

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

person Todd Gardner    schedule 22.06.2009

С BaseA дочерний класс не компилируется, чистая виртуальная функция не определяется

Это верно только в том случае, если вы попытаетесь создать объект BaseA. Если вы создаете объект Child, а затем можете вызвать foo (), используя BaseA * или Child *

В BaseB функция в дочернем элементе вызывается при вызове foo для BaseB * или Child *.

Зависит от типа объекта, так как объект может быть либо BaseB, либо дочерним. Если объект - BaseB, то вызывается BaseB :: foo.

В BaseC функция дочернего элемента вызывается при вызове foo для Child *, но не для BaseB * (вызывается функция в родительском классе).

Да, но вы никогда не захотите этого делать.

person Naveen    schedule 22.06.2009
comment
Есть ли какая-то конкретная причина, по которой я не хочу этого делать? Хотя я не могу, наоборот, привести пример, где я хочу это сделать. Есть ли техническая причина или это просто считается плохой практикой? - person Mizipzor; 22.06.2009
comment
Я не вижу никаких технических причин .. но это действительно сбивает с толку. - person Naveen; 22.06.2009
comment
@mizipzor Может быть, вы наследуете какие-то классы, но у вас нет их кода. Не идеальный дизайн, но такое бывает. - person Kae; 26.07.2014

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

person stefaanv    schedule 22.06.2009

Во многом это зависит от того, как вы это назвали.

если вы сделали:

class Child : public BaseA
{
public:
    int foo() { return 42; }
};

и сделал

BaseA baseA = new Child();
baseA->foo();

Это вызовет функцию Child foo.

Однако, если вы сделали это:

BaseA baseA = new BaseA();

Это приведет к ошибке времени компиляции.

person Daniel    schedule 22.06.2009

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

Это может быть полезно, если вы собираетесь переопределить некоторые функции из Base, а затем снова получить их.

person Eric H.    schedule 22.06.2009