Didii
Didii

Reputation: 1363

Virtual function from child class calls parent function once

This problem seems to be related to threads and running the program very quickly. I have 2 classes, ThreadParent and ThreadChild where one inherits from another. ThreadParent creates a thread and runs the function func, declared as static to circumvent pointer problems. I do however want that the inherited classes such as ThreadChild decide what the thread exactly does so func calls the virtual function Do.

When creating a ThreadChild object however and immediately running the thread calls ThreadParent::Do once at the start, and all following calls are ThreadChild::Do. Interestingly, when I wait a bit before calling Do, it does not happen.

Is there a better solution than to wait a bit? And more importantly, why does this happen?


Here is a small, but complete example. It creates a ThreadChild object which executes Do every 200ms. The program ends after 1s (waiting for an enter press).

#include <iostream>
#include <windows.h>
#include <thread>

// The parent class
class ThreadParent {
protected:
    bool _loopThread; //setting this to false should end the thread
    std::thread _thread; //the thread

public:
    // Basic constructor
    ThreadParent(ThreadParent* child)
        : _loopThread(true),
        _thread(func, child, &_loopThread) {}

    // Stops the thread and waits for it to finish
    void StopThread() {
        _loopThread = false;
        _thread.join();
    }

protected:
    // The function the thread will be running (static because of pointer issues)
    static void func(ThreadParent* child, bool* loopThread) {
        //Sleep(10); //<- uncomment to solve the problem?
        while (*loopThread) {
            child->Do(); // Do is called every 200ms
            Sleep(200);
        }
    }
    // The function which is called repeatedly until _loopThread is set to false
    virtual void Do() {
        std::cout << "Parent call\n";
    }
};

// The child class
class ThreadChild : public ThreadParent {
public:
    // Basic constructor
    ThreadChild()
        : ThreadParent(this) {}

protected:
    // Redefines Do() with another message
    void Do() {
        std::cout << "Child call\n";
    }
};

// The program
int main() {
    ThreadChild thread;  // Create and run the thread
    Sleep(1000);         // Run the thread for 1s
    thread.StopThread(); // End it
    std::cout << "Press <enter> to terminate...";
    std::cin.get(); // Wait for user to end program
    return 0;
}

Output:

Parent call
Child call
Child call
Child call
Child call
Press <enter> to terminate...

Upvotes: 3

Views: 1700

Answers (2)

Vishal
Vishal

Reputation: 158

When the derived class is instantiated, first the base class constructor is called and the vtable pointer vptr is initialized to the base class vtable, which holds a pointer to the ThreadParent::Do. Only when the derived class constructor runs, the vtable pointer vptr is over-written (made to point) to the dervied class vtable, which holds a pointer to ThreadChild::Do.

As a result, calling a virtual method from base class constructor will always call the base class implementation and not the derived class over-ridden method.

This faq explains the same in greater detail

Upvotes: 0

Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17415

During construction, the baseclass subobject is constructed before the derived class. Inside the baseclass body, the dynamic type is actually that of the baseclass, so dynamic function dispatches (virtual function calls) will call the according function of the baseclass. Depending on the timing, you will thus see either function being called.

In order to fix that, just start the thread explicitly in a second initialization function that is called after the construction is complete.

BTW: The static function is a red herring, you don't avoid any mistakes. Also, it's typically a bad idea to create thread hierarchies. Rather, your class instances represent tasks or jobs, which may or may not be executed in a separate thread. Tightly coupling these objects to a thread may be a bad idea. Also, the way you pass a pointer to the baseclass constructor seems fragile, because as it creates a dependency that shouldn't exist in the first place.

Upvotes: 3

Related Questions