Я хочу понять назначение виртуальных функций.
Давайте проанализируем этот код, где функция-член не виртуальная:
ПРИМЕР 1:
struct A
{
void foo1() { cout << "foo 1 from A \n"; }
};
struct B : A
{
void foo1() { cout << "foo 1 from B \n"; }
};
int main()
{
A *a = new B;
a->foo1(); // will print "foo 1 from A \n" because A::foo1 isn't virtual
B *b = new B;
b->foo1(); // will print "foo 1 from B \n"
}
Как мы видим вывод a->foo1()
; будет "foo 1 from A", но я хочу, чтобы функция B выполняла свою собственную функцию foo1
. Поэтому я должен переопределить его и сделать A::foo1
виртуальной функцией.
ПРИМЕР 2:
struct A
{
virtual void foo1() { cout << "foo 1 from A \n"; }
};
struct B : A
{
void foo1() { cout << "foo 1 from B \n"; }
};
int main()
{
A *a = new B;
a->foo1(); // will print "foo 1 from B \n"
B *b = new B;
b->foo1(); // will print "foo 1 from B \n"
}
Теперь A::foo1
был переопределен и a->foo1()
печатает "foo 1 from B", как мы этого хотим. Однако давайте рассмотрим случай, когда в классе B есть некоторые функции, отсутствующие в A:
ПРИМЕР 3:
struct A
{
int a;
void foo1() { cout << "foo 1 from A \n"; }
};
struct B : A
{
int b;
void foo1() { cout << "foo 1 from B \n"; }
void foo2() { cout << "foo 2 from B \n"; }
};
int main()
{
A *a = new B;
// a->foo2(); // compiler error, a doesn't see foo2 function
a->foo1();
// a->b = 1; // compiler error, a doesn't see member variable b
a->a = 1;
// We aren't going to do B *b = new A; here because that's nonsense
B *b = new B;
b->foo2(); // ok
b->foo1(); // ok
b->b = 1; // ok
b->a = 1; // ok
}
Как мы видим, теперь B не является точной копией A; он наследует A и расширяет его новыми функциями и переменными. Мы не можем сделать a->foo2()
или a->b
.
Я думаю, что утверждения типа A *a = new B;
гораздо более запутаны при чтении или анализе кода, чем B *b = new B;
.
Я знаю, что b
- указатель на экземпляр B. Разве вас не смущает вопрос о типе объекта, на который указывает A*
?
Итак, мой вопрос: для чего мы можем использовать виртуальные функции?
Я не буду использовать его с производным классом, если у него есть переменные или функции, отсутствующие в базовом классе. Намного понятнее создать новый объект, используя B *b = new B;
, а не A *a = new B;
.
Когда я создаю экземпляр B, используя B *b = new B;
, функции-члены A не обязательно должны быть виртуальными, потому что B автоматически переопределит A::foo1
своим собственным foo1
.
Поэтому я думаю, что единственное использование для виртуальных функций - это когда мы хотим изменить функциональность во время выполнения.
Возможно, это может быть полезно в таком случае, когда мы хотим изменить исполняемый класс во время выполнения:
A *a;
B b;
C c;
a = &B;
a->foo1();
a = &C;
a->foo1();
В качестве альтернативы, это может быть полезно при передаче аргумента функции:
void execute(A *a)
{
a->foo1();
}
Я что-то упускаю по этому поводу?