虚拟功能和助推绑定奇怪的行为

Virtual function and boost bind strange behavior

本文关键字:绑定 功能 虚拟      更新时间:2023-10-16

我在Linux下编写的一段代码中看到了一个奇怪的行为,我想分享一下,看看是否有人知道原因。我有一个基类和一个派生类。在基类中,我定义了一个虚拟方法,在派生类中,我用相同的签名重新定义了该方法。然后我使用boost绑定来启动一个线程。这是一个示例代码:

Class Base {
public:
    virtual void DoSomething();
    virtual void Init() = 0;
    ...
}
Class Derived : public Base {
public:
    void DoSomething();
    void Init();
    ...
}

在派生类的Init方法中,我这样做了:

 boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this));

基类的DoSomething方法做了它应该做的事情,而派生类的相同方法是一个空方法,错误地留在了那里。现在,在运行上面的代码时,大多数时候基类的DoSomething都是在线程中执行的,因此应用程序运行良好,但有时不起作用。经过一些调试,我注意到了上面的错误,删除派生类的DoSomething解决了这个问题。在调试模式下使用Eclipse,似乎总是调用派生类的DoSomething方法,而从控制台运行应用程序在大多数情况下都有效,但并非总是如此。这种行为有原因吗?我的意思是,为什么绑定函数有时使用基类方法,有时使用派生类的相同方法?

提前感谢

编辑以响应@pmr

很难展示一个完整的工作示例,我将尝试展示一些类是如何使用的。

首先我实例化一个Derived对象,然后在init函数中用上面显示的初始化代码启动线程。DoSomething有一个在向量上迭代的while循环,但我认为这不是重点。

void Derived::Init()
{
    ...
    boost::thread *t = new boost::thread(boost::bind(&Base::DoSomething, this));
}
void Base::DoSomething()
{
    while(true) {
        ...
    }
}
void Derived::DoSomething()
{
}

正如您在这段代码中看到的,Derived DoSomething方法是空的,所以有时我没有看到任何处理,而是在Base DoSometing函数中进行。

这里有一个疯狂的猜测:用于启动线程的对象实际上被破坏了!由于虚拟函数的绑定在销毁过程中发生了变化(当对象被销毁时,所有虚拟函数都会解析为所使用的对象是当前正在销毁的类的类型)。为此,"vtable指针"通常会重置为指向合适的"虚拟函数表"。一旦基地被摧毁,就不需要进一步摧毁物体了。

这与您对行为随机性的解释非常吻合:有时父线程执行得足够快,可以到达基类构造函数,有时则不然。在使用调试模式进行编译时,父线程显然在销毁对象之前持续了很长时间。你所说的在很多情况下一切都很好,这也并没有真正破坏这一形象:通常有缺陷的代码看起来似乎是有效的,尽管在更仔细地检查时,它实际上显示出了错误的行为。

我想我已经找到了这种行为的原因:起初我在基类构造函数中调用了线程构造函数。我认为这就是问题所在,因为基构造函数是在派生构造函数之前调用的,有时vtable是指向空的派生函数创建的,有时线程是在vtable创建之前启动的,因此bind函数使用了基方法,这就是它的目的。我想使用debug会引入一些延迟,所以使用调试器时线程总是绑定到派生类方法,从而导致错误的行为。此外,我还尝试将线程创建转移到init函数中,这样就总是调用派生函数。

我们在内部操作系统中遇到了同样的问题,在构建对象时,我们将一些虚拟函数绑定到另一个工作线程。如果操作系统在派生类的构造函数之前切换到工作线程,工作线程将使用基类类型调用"this"。所以,我认为我们可以描述它:"this"指针在构造函数和去构造函数中是不线程安全的。