Добро пожаловать в StackOverflow!
Что делают виртуальные методы, так это позволяют вам вызывать свой метод на основе фактического экземпляра класса, а не типа указателя.
Итак, в вашем случае root->foo()
вызовет метод, основанный на фактическом классе, и не всегда вызовет реализацию node
.
Однако, чтобы вызвать реализацию derivedNode
, вы должны создать его экземпляр! Как и сейчас, ваш derivedTree
использует базовый конструктор tree
, который напрямую создает экземпляр node
, поэтому оба derivedTree
и tree
будут иметь объект node
как root
!
Чтобы решить проблему, как уже показано в других ответах, вы можете добавить к tree
конструктор, который принимает внешний node
указатель, и использовать этот конструктор в конструкторе derivedTree
для инициализации root
с указателем на derivedTree
.
Вот так: ( исполняемая ссылка )
class tree {
public:
class node {
public:
virtual int foo() { return 1; }
node() = default;
virtual ~node() = default;
};
node* root;
tree(): root(new node) {};
tree(node* d) : root(d) {}; // here we initialize the root pointer with a given pointer
virtual ~tree() { delete root; };
};
class derivedTree : public tree {
public:
class derivedNode : public node {
public:
virtual int foo() { return 2; }
};
derivedTree(): tree(new derivedNode) {}; // here we use the added constructor to create a derivedNode and set it as root
};
using std::cout;
int main() {
tree t1;
derivedTree t2;
cout << "Hey! " << t1.root->foo() << "\n";
cout << "Hey! " << t2.root->foo() << "\n";
}
Обратите внимание, что невозможно инициализировать root
непосредственно в производный конструктор, потому что язык позволяет помещать в список инициализаторов только поля фактического класса, а не производные поля, и в зависимости от компилятора вы рискуете создать утечку памяти.