0
votes

I am trying to call virtual method at the constructer of parent class and I want to that of child methods. Let me explain:

I need to read words from line by line from a text file and insert them a search tree one by one. I have tree child classes: DictionaryBST,DictionaryAVLTree,Dictionary23Tree. I implemented their own insert methods.

Here the header code of my parent class which is 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);
};

Here is the constructer:

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();
}

My main method:

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

I don't want to write constructer methods to each one, as well. Can some help me?

2
@yano dynamic_casting the this pointer won't work inside the base class's constructor and destructor.Remy Lebeau
@RemyLebeau yes, I was fuzzy on that and remembered when I read your answer. The child constructor hasn't finished, makes sense.yano
Actually, I understood what you mean and it seems logical. But I want to know ,as well, that is there a way to do it like what I explain?Furkan Calik
The parent class has no idea of child methods, nor how many children there are. There could be zero children. The parent can't be selective and discern which child called it.Thomas Matthews
@FurkanCalik No, it is not possible like you want, because the child portion of the this object simply does not exist yet, it hasn't even started being constructed yet, while the parent constructor is running, thus you can't call child methods on it. You have to wait until the child constructor is running/exited before you can call any child methods on the object.Remy Lebeau

2 Answers

3
votes

Don't call virtual functions in a constructor or destructor. The reason for this is that during the constructor of DictionarySearchTree, the runtime type of the current object this is always DictionarySearchTree and never any more derived type. This means that virtual function calls made during the constructor will always be bound to those defined or inherited by DictionarySearchTree, and nothing more. There are good reasons for this and they're discussed further in this Q/A

The best thing in your case would be to populate the dataset after constructing the most-derived object. For example, you could add a void populate(string dictionaryFile) member function to DictionarySearchTree that calls all the virtual member functions you want. Then, importantly, call this populate() function after you have constructed the most derived object, as a separate step.

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");
}

Note that smart pointers such as std::unique_ptr should be prefered over raw owning pointers for dynamic memory management.

The ISO C++ FAQ on Strange Inheritance also discusses this issue and suggests two-phase initialization as I've shown here as a work-around.

1
votes

A base class constructor (and destructor) cannot call virtual methods of a derived class.

In the base class constructor, the derived class portion of the object has not been constructed yet.

In the base class destructor, the derived class portion of the object has already been destroyed.

In both cases, the this pointer is pointing at the base class portion of the object, not the derived portion. If there is a vtable involved (which is usually the case in polymorphism, but that is not guaranteed by the C++ standard), it is pointing at the base class's vtable, not the derived class's vtable.

You need to rethink your design. For instance, by moving the tree-management methods into a separate container class that manages a pointer to the actual tree and can then create instances of your polymorphic classes as needed. Such as by taking in a template parameter to specify the class type of the tree. Or determining the class type to use based on the contents of the text file. Etc.