Понимание (просто?) C ++ Наследование - PullRequest
17 голосов
/ 05 мая 2011

Я немного пытаюсь понять, почему этот фрагмент кода не компилируется.

#include <cstdio>

class A {
public:
    virtual int potential()=0;
    virtual int potential(int arg, int arg2)=0;
};

class B : public A {
public:
    int potential() { return 1; }
    virtual int potential(int arg, int arg2) { return 2; }
};

class C : public B {
public:
    int potential(int arg, int arg2) { return 3; }
};


int main(int argc, char** argv) {
    C c;
    int value = c.potential();
    printf("Got %i\n", value);
    return 0;
}

У меня есть два чисто виртуальных метода, оба названы potential в абстрактном суперклассе A. Подкласс B затем определяет оба, но дальнейший подкласс C должен только переопределить один из методов.

Однако при компиляции распознается только метод, определенный в C, а potential() не виден (это должно было быть унаследовано от B):

In function 'int main(int, char**)':
Line 23: error: no matching function for call to 'C::potential()'
compilation terminated due to -Wfatal-errors.

Если я переименую A::potential(int, int) во что-то другое по всему дереву наследования, например A::somethingElse(int, int), тогда код скомпилируется нормально, и на выходе получится Got 1, как и ожидалось.

Это было проверено с использованием clang , g ++ и MSVC cl .

Есть идеи о том, что происходит?

Ответы [ 2 ]

26 голосов
/ 05 мая 2011

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

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

Скрытие происходит из-за способа, которым C ++ разрешает (перегружает) имена методов: когда вы вызываете метод potential в экземпляре класса (здесь c), C ++ ищет в классе, является ли метод с таким именем существует. Если это не так, поиск продолжается в базовых классах. Он идет вверх по иерархии, пока не будет найден хотя бы один метод с таким именем.

Но в вашем случае C ++ не нужно далеко искать: метод уже существует в C, поэтому он останавливает поиск. Теперь C ++ пытается сопоставить сигнатуру метода. К сожалению, подпись метода не совпадает, но в настоящее время уже слишком поздно: не удается разрешить перегрузку; C ++ не ищет другие подходящие методы.

Существует три решения:

  1. Импортируйте его с using в C:

    class C : public B {
    public:
        using B::potential;
        int potential(int arg, int arg2) { return 3; }
    };
    
  2. Вызовите метод из экземпляра базового класса в main:

    C c;
    B& b = c;
    int value = b.potential();
    
  3. Уточните имя явно в main:

    C c;
    int value = c.B::potential();
    
11 голосов
/ 05 мая 2011

Проблема заключается в сокрытии имени.

Перегрузки функций и наследование функций не лучшие друзья.Обычно вы либо [хмм, а что значит «либо» для трех?] :

  • Перегрузить функцию внутри одного класса.Все отлично работает.
  • Унаследуйте незагруженную функцию от базового класса.Все работает нормально.
  • Повторно реализовать не перегруженную функцию из базового класса B в производном классе C.C::func скрывает B::func, поскольку оно имеет то же имя.

Вы используете наследование и перегрузку, а ваш C::func скрываетсяB::func и все его перегрузки , даже те, которые не были повторно реализованы в C.

Это немного странная путаница в C ++, но ее легко разрешить.

Короче говоря, решение состоит в том, чтобы ввести B::potential() в область действия C с помощью оператора using:

class C : public B {
public:
    using B::potential; // brings overloads from B into scope
    int potential(int arg, int arg2) { return 3; }
};

Я написал статью здесь , которая демонстрирует проблему в глубине.

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