C ++ Виртуальная функция скрыта - PullRequest
17 голосов
/ 18 июля 2011

У меня проблема с наследованием C ++.

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

class A {
public:
   virtual void onFoo() {}
   virtual void onFoo(int i) {}
};

class B : public A {
public:
    virtual void onFoo(int i) {}
};

class C : public B {
};


int main() {
    C* c = new C();
    c->onFoo(); //Compile error - doesn't exist
}

Мой вопрос: почему это не компилируется? Насколько я понимаю, C должен наследовать обе функции onFoo от A - и на самом деле это компилируется, если вы удалите переопределение onFoo в B - но g ++ выдает ошибку, что C не имеет функции onFoo ().

Ответы [ 6 ]

38 голосов
/ 18 июля 2011

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

В вашем конкретном случае у вас есть c->onFoo(); и c типа C. Компилятор не видит никакого объявления onFoo в C, поэтому он продолжает вверх в иерархии. Когда компилятор проверяет B, он видит, что на этом уровне есть объявление void onFoo(int i), поэтому он останавливает поиск и пытается разрешить перегрузку. В настоящее время разрешение перегрузки не выполняется из-за несогласованности аргументов.

Тот факт, что объявление void onFoo(int) присутствует на уровне B, имеет эффект , скрывающий остальные перегрузки в любом базовом классе, так как он остановит поиск. Обратите внимание, что это проблема с неквалифицированным поиском, функция все еще там и применима к объекту, но не будет найдена при обычном поиске (вы все равно можете назвать ее как c->A::onFoo()).

Что касается того, что делать с скрытием , то самый простой способ - использовать объявление using для переноса функций в область действия:

class B : A {
public:
   using A::onFoo; // All A::onFoo overloads are *considered* here
   void onFoo( int );
};

Эффект объявления using здесь заключается в том, что при поиске класса B в поиске идентификатора onFoo компилятор также учитывает все перегрузки onFoo в базовом классе. , включив обычный поиск, можно найти A::onFoo().

12 голосов
/ 18 июля 2011

Если вы хотите, чтобы члены базового класса перегружали производные члены класса, вы должны использовать using:

struct A
{
   virtual void onFoo() {}
   virtual void onFoo(int i) {}
};

struct B : A
{
    using A::onFoo;
    virtual void onFoo(int i) {}
};

struct C : B
{
};


int main()
{
    C* c = new C();
    c->onFoo();
}
7 голосов
/ 18 июля 2011

Это скрытие имени, в основном в B существуют только объявленные переопределения, а остальные перегрузки в A скрыты.

2 голосов
/ 18 июля 2011

Методы класса А и В должны быть общедоступными.Это, и вы пропускаете точки с запятой в конце каждого объявления класса.

class A {
public:
   virtual void onFoo() {}
   virtual void onFoo(int i) {}
};

class B : public A {
public:
    virtual void onFoo(int i) {}
};

class C : public B {
};


int main() {
    C* c = new C();
    c->onFoo(); //Compile error - doesn't exist
}
1 голос
/ 18 июля 2011

Вы забыли модификатор public: перед методами в обоих классах A и B. Поэтому метод onFoo является закрытым и поэтому не виден нигде за пределами этих классов.

0 голосов
/ 21 февраля 2013

Полагаю, вы пропустили добавление этого в class B:

struct B : A
{
    using A::onFoo;
    virtual void onFoo(int i) {}
    void onFoo() {} //This line missing in your code.
};

Теперь это компилируется!

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