子类中的虚拟函数调用父函数一次

Virtual function from child class calls parent function once

本文关键字:一次 函数 虚拟 函数调用 子类      更新时间:2023-10-16

这个问题似乎与线程和运行程序的速度非常快有关。我有 2 个类,ThreadParent 个类和ThreadChild类,一个从另一个继承。 ThreadParent创建一个线程并运行函数func,声明为静态以规避指针问题。但是,我确实希望继承的类(例如ThreadChild)决定线程究竟做什么,func调用虚拟函数Do

但是,当创建ThreadChild对象并立即运行线程调用时,ThreadParent::Do开始时调用一次,并且所有后续调用都将ThreadChild::Do。有趣的是,当我在打电话之前等待Do时,它不会发生。

有没有比等待更好的解决方案?更重要的是,为什么会发生这种情况?


这是一个小而完整的示例。它创建一个ThreadChild对象,每 200 毫秒执行一次Do。程序在 1 秒后结束(等待进入按)。

#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 calln";
    }
};
// The child class
class ThreadChild : public ThreadParent {
public:
    // Basic constructor
    ThreadChild()
        : ThreadParent(this) {}
protected:
    // Redefines Do() with another message
    void Do() {
        std::cout << "Child calln";
    }
};
// 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;
}

输出:

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

在构造过程中,基类子对象是在派生类之前构造的。 在基类主体内部,动态类型实际上是基类的类型,因此动态函数调度(虚函数调用)将调用基类的相应函数。因此,根据时间的不同,您将看到正在调用的任一函数。

为了解决这个问题,只需在构造完成后调用的第二个初始化函数中显式启动线程。

顺便说一句:static功能是一个红鲱鱼,你不会避免任何错误。此外,创建线程层次结构通常是一个坏主意。相反,类实例表示任务或作业,它们可能会也可能不会在单独的线程中执行。将这些对象紧密耦合到线程可能是一个坏主意。此外,将指针传递给基类构造函数的方式似乎很脆弱,因为它创建了一个本来就不应该存在的依赖项。

实例化派生类时,首先调用基类构造函数,并将 vtable 指针vptr初始化为基类 vtable,该指针包含指向ThreadParent::Do的指针。仅当派生类构造函数运行时,vtable 指针vptr才会被覆盖(指向)派生类 vtable,该类包含指向 ThreadChild::Do 的指针。

因此,从基类

构造函数调用虚拟方法将始终调用基类实现,而不是派生类重写的方法。

此常见问题解答更详细地解释了相同的内容

相关文章: