Ваш класс B не переопределяет функцию-член в A, он перегружает это. Или, в любом случае, попытайтесь позже увидеть немного о сокрытии.
Переопределение - это когда производный класс определяет свою собственную версию виртуальной функции-члена из базового класса. Перегрузка - это когда вы определяете разные функции с одинаковыми именами.
Когда виртуальный вызов выполняется для указателя или ссылки, которая имеет тип базового класса, он будет «учитывать» только переопределения в производном классе, но не перегрузки. Это очень важно - для того, чтобы вызывающий объект обрабатывал экземпляр B так, как будто он делает все, что может сделать A (что является точкой динамического полиморфизма и виртуальных функций), его функция hello
должна иметь возможность принимать любой тип A. Функция A hello
, которая принимает только объекты типа B, а не A, является более строгой. Он не может играть роль функции hello
, поэтому он не переопределен.
Если вы немного поэкспериментируете с вызовом hello
для A и B, передавая объекты типа A или B, вы сможете увидеть разницу. A имеет функцию, принимающую A (которую вы не определили, поэтому, если вы ее вызовите, ваша программа не сможет связать, но вы можете это исправить). В B есть функция, принимающая B. Они имеют одно и то же имя, и, конечно, поскольку B наследуется от A, вы можете передать B функции, принимающей A. Но функция B не действует как переопределение в виртуальных вызовах. .
Можно вызывать функцию A для объекта B, но только через ссылку или указатель на A. Особенностью C ++ является то, что определение hello
в B скрывает определение в A. Если перегрузка - это то, что вам нужно хотите, можно скрыть функцию базового класса, добавив using A::hello;
к классу B. Если вы хотите переопределить то, что вам нужно, вы должны определить функцию, принимающую те же параметры. Например:
#include <iostream>
class A
{
public:
virtual int hello(A a) {std::cout << "A\n"; }
virtual int foo(int i) { std::cout << "A::Foo " << i << "\n"; }
};
class B : public A
{
public:
using A::hello;
// here's an overload
int hello(B b){ std::cout << "B\n"; };
// here's an override:
virtual int foo(int i) { std::cout << "B::Foo " << i << "\n"; }
};
int main() {
A a;
B b;
a.hello(a); // calls the function exactly as defined in A
a.hello(b); // B "is an" A, so this is allowed and slices the parameter
b.hello(a); // OK, but only because of `using`
b.hello(b); // calls the function exactly as defined in B
A &ab = b; // a reference to a B object, but as an A
ab.hello(a); // calls the function in A
ab.hello(b); // *also* calls the function in A, proving B has not overridden it
a.foo(1); // calls the function in A
b.foo(2); // calls the function in B
ab.foo(3); // calls the function in B, because it is overridden
}
Выход:
A
A
A
B
A
A
A::Foo 1
B::Foo 2
B::Foo 3
Если вы удалите строку using A::hello;
из B, то вызов b.hello(a);
не будет скомпилирован:
error: no matching function for call to `B::hello(A&)'
note: candidates are: int B::hello(B)