Переопределение функции C ++ - PullRequest
19 голосов
/ 22 июня 2009

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

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 * (вызывается функция в родительском классе).

Ответы [ 6 ]

16 голосов
/ 22 июня 2009

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

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

14 голосов
/ 22 июня 2009

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

  • При BaseA он будет компилироваться и выполняться как задумано, при этом foo() является виртуальным и выполняется в классе Child.
  • То же самое с BaseB, он также будет компилироваться и выполняться, как предполагалось, с foo(), являющимся virtual () и выполняющимся в классе Child.
  • Однако с BaseC он будет компилироваться и выполняться, но он будет выполнять версию BaseC, если вы вызываете ее из контекста BaseC, и версию Child, если вы вызываете с контекстом Child.
2 голосов
/ 22 июня 2009

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

2 голосов
/ 22 июня 2009

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

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

С BaseB, функция у ребенка вызывается при вызове foo на BaseB * или ребенок *.

Зависит от типа объекта, поскольку объект может быть BaseB или Child. Если объектом является BaseB, то вызывается BaseB :: foo.

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

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

1 голос
/ 22 июня 2009

Это в основном зависит от того, как вы его назвали.

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

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

и сделал

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

Это вызвало бы функцию foo ребенка.

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

BaseA baseA = new BaseA();

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

0 голосов
/ 22 июня 2009

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

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

...