OOP - 如何在 parent class 处调用 child 方法

OOP - How to call child method at parent class

我正在尝试在 parent class 的构造函数中调用虚方法,我想调用 child 方法的构造函数。让我解释一下:

我需要从文本文件中逐行读取单词,然后将它们一个接一个地插入到搜索树中。我有树 child classes: DictionaryBST,DictionaryAVLTree,Dictionary23Tree。我实现了他们自己的插入方法。

这里是我的 parent class 的 header 代码,即 DictionarySearchTree:

class DictionarySearchTree {
public:

    DictionarySearchTree();
    DictionarySearchTree(string dictionaryFile);
    virtual ~DictionarySearchTree();
    virtual void insert(string word);
    virtual void search(string word, int& numComparisons, bool& found) const;
    virtual void search(string queryFile, string outputFile) const;
    virtual void inorderTraversal();

protected:
    TreeNode* root;
    int size;

    void searchNode(TreeNode* node, string word, int& numComparisons, bool& found) const;
    void virtual insertNode(TreeNode*& node, string word);
    void postTraversalDeletation(TreeNode*& node);
    void inorder(TreeNode* node);
    void getHeight(TreeNode* node, int& height);
};

这是构造函数:

DictionarySearchTree::DictionarySearchTree(string dictionaryFile) {

    root = NULL;
    size = 0;
    
    istringstream stream;
    ifstream infile;
    string line;
    infile.open(dictionaryFile);

    while (getline(infile, line)) {
        insert(line);    // This methods should call child's ones.
    }

    infile.close();
}

我的主要方法:

int main() {
    DictionarySearchTree* tree = new DictionaryBST("./dictionary.txt");
    DictionarySearchTree* avlTree = new DictionaryAVLTree("./dictionary.txt");
    DictionarySearchTree* twoThreeTree = new Dictionary23Tree("./dictonary.txt");
}

我也不想为每一个都编写构造方法。有人能帮帮我吗?

基 class 构造函数(和析构函数)不能调用派生 class 的虚方法。

在基 class 构造函数中,对象的派生 class 部分尚未构造。

在基础 class 析构函数中,对象的派生 class 部分已经被销毁。

在这两种情况下,this 指针都指向对象的基础 class 部分,而不是派生部分。如果涉及到 vtable(这在多态性中通常是这种情况,但 C++ 标准不保证),它指向基 class 的 vtable,而不是派生的 class' s vtable.

您需要重新考虑您的设计。例如,通过将树管理方法移动到一个单独的容器 class 中,该容器管理指向实际树的指针,然后可以根据需要创建多态 classes 的实例。例如通过接受模板参数来指定树的 class 类型。或者根据文本文件的内容确定要使用的 class 类型。等等

不要在构造函数或析构函数中调用 virtual 函数。原因是在 DictionarySearchTree 的构造函数中,当前对象 this 的运行时类型总是 DictionarySearchTree 并且永远不会再派生类型。这意味着在构造函数期间进行的 virtual 函数调用将始终绑定到那些由 DictionarySearchTree 定义或继承的函数,仅此而已。这样做有充分的理由,并且会进一步讨论 in this Q/A

在您的情况下,最好的办法是在构建派生最多的对象后填充数据集。例如,您可以将 void populate(string dictionaryFile) 成员函数添加到 DictionarySearchTree 以调用您想要的所有 virtual 成员函数。然后,重要的是,在 构造了最派生的对象之后调用此 populate() 函数 ,作为一个单独的步骤。

int main() {
    std::unique_ptr<DictionarySearchTree> tree = std::make_unique<DictionaryBST>();
    std::unique_ptr<DictionarySearchTree> avlTree = std::make_unique<DictionaryAVLTree>();
    std::unique_ptr<DictionarySearchTree> twoThreeTree = std::make_unique<Dictionary23Tree>();

    tree.populate("./dictionary.txt");
    avlTree.populate("./dictionary.txt");
    twoThreeTree.populate("./dictionary.txt");
}

请注意,对于动态内存管理,智能指针(如 std::unique_ptr 应优先于原始拥有指针。

ISO C++ FAQ on Strange Inheritance 也讨论了这个问题并建议进行两阶段初始化,正如我在此处显示的那样。