Переопределение функции-члена с производным типом в C ++ - PullRequest
0 голосов
/ 22 февраля 2020
  • Возможно переопределить параметры с помощью производного класса, подобного тому, что показано в B::g() или B::h()?

  • Если нет разумного способа достичь того же самого?

  • Имеет смысл, почему B::i() не работает, но почему не другие?

Моя цель - использовать r в B::g() как тип Y.

struct X {};
struct Y : X {};

struct A {
    virtual void f(X q);
    virtual void g(X& r);
    virtual void h(X* s);

    virtual X i();  // note: overridden function is 'virtual X A::i()'
    virtual X* j();
};

struct B final : A {
    void f(Y q) final;   // error: 'void B::f(Y)' marked 'final', but is not virtual
    void g(Y& r) final;  // error: 'void B::g(Y&)' marked 'final', but is not virtual
    void h(Y* s) final;  // error: 'void B::h(Y*)' marked 'final', but is not virtual

    Y i() final;   // error: invalid covariant return type for 'virtual Y B::i()'
    Y* j() final;  // works
};


Ответы [ 5 ]

0 голосов
/ 22 февраля 2020

Переопределение виртуальной функции может быть выполнено только в том случае, если переопределенная функция имеет ту же сигнатуру (тип аргументов, const квалификаторы и т. Д. c), что и унаследованную.

Итак

* Возможно 1005 *

, но изменение подписи f(), как в

 struct B : public A
 {
      virtual void f(Y &);
 };

, фактически скрывает унаследованный f(), а не переопределяет его. С помощью идентификатора override компилятор обнаружит, что это не переопределение, и диагностирует ошибку.

Можно передать Y в A::f() или B::f(), если правильно переопределить, поскольку ваш Y является производным от X. Но невозможно переопределить A::f() с помощью функции, у которой любой аргумент отличается от A::f().

Существует особый случай для функции, которая возвращает указатель (или, альтернативно, ссылку ) к базовому классу - это может быть переопределено функцией, которая возвращает указатель на производный класс. Ограничение на то, что аргументы должны иметь один и тот же тип (ы). Таким образом, ваш B::j() работает, поскольку Y является производным от X. Что касается компилятора, то вызов типа

 X*x = pA->j();    // pA is of type A* but points at a B

в порядке, поскольку он по-прежнему возвращает указатель, который может быть корректно преобразован в X *

0 голосов
/ 22 февраля 2020

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

Давайте рассмотрим следующий пример с предоставленные вами классы:

B b;
A *a = &b;

X x;
a->g(x);

В последней строке нет способа гарантировать, что вызывающий g() пройдет Y, как ожидается B, поскольку вызывающий использует базовый класс ' интерфейс, который представляет собой суть виртуальных функций и динамического полиморфизма c в целом.

С другой стороны, для возвращаемого типа безопасно быть более конкретным c в производном классе:

B b;
A *a = &b;
X *x = a->j();

Последнее утверждение все еще безопасно для типов, даже если j() фактически возвращает Y*. Эта особенность C ++ называется ковариантный тип возврата .

Вы также можете прочитать больше о ковариации и контравариантности в системах типов здесь .

0 голосов
/ 22 февраля 2020

Моя цель - использовать r в B :: g () как тип Y.

Вы можете сделать это, но тогда B::g не отменяет A::g. Если это так, вам нужно использовать те же типы параметров:

struct A {
    virtual void g(X& r);
    virtual ~A(){}
};

struct B : A {
    void g(X& r) override;
};
0 голосов
/ 22 февраля 2020

Чтобы переопределить помеченные как final, вам нужно добавить виртуальный в начале.

Например: virtual void f(Y q) final;

B::i() не работает, потому что тип возвращаемого значения не идентичен типу возвращаемого значения функции, которую вы пытаетесь переопределить.

Но причина, по которой B::j() работает, заключается в том, что возвращаемый тип - это указатель, который вы можете технически вернуть указатель на каждый тип есть.

0 голосов
/ 22 февраля 2020

Для переопределения функции список параметров должен быть таким же , но в вашем случае это не так. Вот почему она считается новой функцией и не будет переопределять функцию в родительском.

Поэтому вам нужно иметь что-то подобное для переопределения:

struct A {
    virtual void f(X q);
    virtual void g(X& r);
    virtual void h(X* s);

    virtual X i();  
    virtual X* j();
};

struct B final : A {
    void f(X q) override final;   
    void g(X& r) override final;  
    void h(X* s) override final;  

    X i() override final;   
    X* j() override final;  
    // Y* j() override final;  <-- Also works because of covariant return
};

Но помните, что вы все равно можете передать Y этим функциям, потому что Y наследует X.

В качестве примечания для последнего члена класса, Y* j() override final; также будет работать, потому что это будет Ковариант возврата . Для ковариантного возврата, ваш возврат должен быть указателем или ссылкой. Но вам нужно посмотреть, действительно ли вам это нужно или нет.

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