Производный класс, вызывающий метод родительского класса, который вызывает переопределенный виртуальный метод, вызывает неправильный метод - PullRequest
0 голосов
/ 28 сентября 2018

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

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

Вот код:

#include <iostream>

class Parent
{
public:
    virtual void foo()
    {
        std::cout << "Parent's foo" << std::endl;
    }
    void bar()
    {
        foo();
    }
};

class Child : public Parent
{
public:
    void foo()
    {
        std::cout << "Child's foo" << std::endl;
    }
};

int main()
{
    Child c;

    c.bar();

    return 0;
}

Когда запускается приведенный выше код, он печатает Child's foo.

Однако тот же код, НО с определением foo дочерних классов void foo(bool def = true) Распечатывает Parent's foo.

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

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

Это чисто функция, когда функция переопределяется в C ++.

В C ++ переопределение функции выполняется в соответствии с «сигнатурой» функций-членов:

  • unqualifiedname
  • точный список параметров в объявлении (исключая неявный this)
  • квалификация this неявного параметра

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

[Замечание о квалификации cv для параметров:

Параметры в объявлении , как видит вызывающая сторона , должны быть точно такими же, то есть после удаления бессмысленных cv-квалификаторов на копиях объектов: это cv-квалификаторы для локальных переменных внутритело функции, и это имеет смысл только в определении функции.

void f(const int i); // takes an int by value; const is meaningless
void f(int i); // redeclaration of the same function

// redeclaration of the same function yet again
void f(const int ci) { // const is meaningful here
  ci = 1; // error
}

- примечание конца]

Однако тот же код, НО сдетские классы фуопределение void foo(bool def = true) Печатает foo Родителя.

Поскольку нет совпадения списков параметров: пустой список параметров совпадает только с пустым списком параметров .

Здесь необходимо заменить аргумент по умолчанию двумя перегруженными функциями, без переадресации на другую:

void foo(bool def); // new function signature 

void foo() { // overrides Parent's member
  foo(true);
}

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

Это было печальное положение дел после первой версииСтандарт C ++ был формализован.Теперь все по-другому.

Если вы хотите быть уверены, что действительно переопределяете объявление базового класса, теперь вы можете использовать ключевое слово override:

class Child : public Parent
{
public:
    void foo(bool def);
    void foo() override {
      foo(true);
    }
};
0 голосов
/ 28 сентября 2018

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

#include <iostream>
class Parent
{
public:
    virtual void foo()
    {
        std::cout << "Parent's foo" << std::endl;
    }
    void bar()
    {
        foo();
    }
};

class Child : public Parent
{
public:
    virtual void foo(bool def) // virtual if another subclass needs to override
    {
        std::cout << "Child's foo def = " << def << std::endl;
    }
    virtual void foo()override //override and virtual optional here
    {
        foo(true);
    }
};

int main()
{
    Child c;

    c.bar();
    c.foo();
    c.foo(true);

    return 0;
}
...