Не вызывается виртуальная функция наследуемого внутреннего класса
Имеются 2 класса, второй наследуется от первого. Внутри каждого из них есть по одному вложенному классу, второй также наследуется от первого. Они должны отличаться ТОЛЬКО функцией печати.
#include <iostream>
using namespace std;
class A { // базовый класс
public:
class InnerA { // внутренний класс
public:
void print() {
cout << "Bad! Inner A" << endl;
}
};
InnerA in; // объект внутреннего класса
virtual void foo() { // функция с одинаковым кодом для классов A и B
// do something
in.print();
// do something else
}
};
class B : public A { // наследник, отличающийся только методами print()
public:
class InnerB : public InnerA { // наследник внутреннего класса, отличающийся методом print()
public:
void print() {
cout << "Good! Inner B, not A" << endl;
}
};
InnerB in; // объект внутреннего класса другого типа (переопределили?)
};
int main() {
B b; // объект класса B
b.foo(); // выводит "Bad! Inner A", вместо "Good! Inner B, not A"
}
typeid(b.in) говорит, что это действительно InnerB
При этом объект класса B ведёт себя так же, как и объект класса A, хотя функция print() должна быть переопределена
P.S. Если просто скопировать функцию foo() в класс B, всё работает как надо. Но дублирование всего кода -- плохое решение.
Ответы (2 шт):
Это потому, что вы вызываете функцию foo() из класса A. Класс A ничего не знает о классе B и вызывает print() из собственного вложенного объекта класса InnerA in;.
Вам нужно ещё перегрузить функцию foo() в классе B. Вы же сделали её виртуальной, но почему-то не переопределили.
И поскольку foo() будет иметь доступ к обоим объектам A::in B::in лучше указывать от какого именно in вы хотите вызвать print(). Хотя в данном примере если написать void foo() override { in.print(); } будет вызван B::in.print();
class A
{
public:
class InnerA
{ public:
void print() { cout << "Bad! Inner A" << endl; }
};
InnerA in; // объект внутреннего класса
virtual void foo() { in.print(); }
};
class B : public A
{
public:
class InnerB : public InnerA
{
public:
void print() { cout << "Good! Inner B, not A" << endl; }
};
InnerB in;
void foo() override { B::in.print(); }
};
А вообще вы путаете виртуальные функции и вложенность объектов. В общем случае, виртуальная функция вызовется в зависимости от типа объекта.
class A
{
public:
virtual void foo() { cout << "class A" << endl; }
};
class B : public A
{
public:
void foo() override{ cout << "class B" << endl; }
};
main()
{
A *ptr = new B;
ptr->foo(); // вызовется B::foo(), хотя указатель типа A
}
В примере у внутреннего класса нет никаких виртуальных функций. InnerB in; ничего не переопределяет, а просто создает еще одно поле, которое затем никак не используется. Тут имело бы смысл передавать указатель на реализацию InnerA - получилось бы почти Dependency Injection без дублирования кода и объектов в классах A и B, а то и вообще можно было бы избавиться от класса B, так как вся логика в наследнике InnerA:
#include <iostream>
#include <memory>
#include <utility>
class A
{ // базовый класс
public: class InnerA
{ // внутренний класс
public: virtual void print()
{
::std::cout << "Bad! Inner A" << ::std::endl;
}
public: virtual ~InnerA() {}
};
private: ::std::unique_ptr<InnerA> m_in; // объект внутреннего класса
public: explicit A(::std::unique_ptr<InnerA> in)
: m_in{::std::move(in)}
{}
public: void foo()
{ // функция с одинаковым кодом для классов A и B
// do something
m_in->print();
// do something else
}
};
class B : public A
{ // наследник, отличающийся только методами print()
public: class InnerB : public InnerA
{ // наследник внутреннего класса, отличающийся методом print()
public: void print() override
{
::std::cout << "Good! Inner B, not A" << ::std::endl;
}
};
public: explicit B()
: A{::std::unique_ptr<InnerA>{new InnerB{}}}
{}
};
int main()
{
B b; // объект класса B
b.foo(); // выводит "Good! Inner B, not A"
}