从父 class 调用派生成员函数

Call a derived member function from parent class

我需要在线程成员函数中调用B::f()。我得到的是“调用的纯虚方法”。我该怎么做?

我假设这一切都是因为 A 初始化列表中的 &A::thread_f,我在其中明确命名了一个范围。

class A {
protected:
    std::thread _thread;

    A() : _thread(&A::thread_f, this) { }

    ~A() { 
        _thread.join();
    }

    virtual void f() = 0;

    void thread_f() {
        f();
    }
};

class B : public A {
protected:
    void f() override {
        std::cout << "B::f()" << std::endl;
    }
};

正如@Evg所说,通过启动一个线程,A ctor在构造B之前调用B的成员函数f

解决方法是在构造完B之后再启动线程

#include<iostream>
#include<vector>
#include <stdlib.h>
#include <thread>

using namespace std;
class A {
public:
    ~A() {
        _thread.join();
    }
    void start()
    {
        _thread = std::thread(&A::thread_f, this);
    }
protected:
    std::thread _thread;
    A()
    {
    }

    virtual void f() = 0;
    void thread_f() {
        f();
    }
};

    class B : public A {
    protected:
        void f() override
        {
            std::cout << "B::f()" << std::endl;
        }
    };

void main() {
    A *pA=new B;
    pA->start();
    delete pA;
}

这不起作用的原因是 c++ 的 order of construction。当构造 class 时,任何基础 class 都会在派生 class.

之前构造

这意味着当你在你的基class中调用虚函数f()时,classB还没有被构造。这意味着唯一可用的选项是尝试调用 A 的 f() 实现。这里的问题是 A 没有 f() 的实现,因此发生错误。

解决方法请看here。我建议将您对 f() 的调用移动到一个单独的初始化阶段,该阶段可以在构造后调用,这意味着基础和派生 classes 都将在调用时完全构造。

这里有两个问题,都与没有同步有关。

第一个问题是可以对尚未完全构建的对象进行虚拟调用。 之前创建了一个新线程 B 的构造,在该线程内部调用了 f() 并且无法保证到那时 B 是完全构建。行为未定义。

进入一些实现细节,虚拟调用通常使用虚拟 tables 实现。指向虚拟 table 的指针在 B 的构造期间由编译器设置,如果您在正确设置该指针之前调用 f(),您可能会调用 A::f() 而不是 B::f()。调用 A::f() 会生成“调用纯虚方法”错误消息。请注意,f() 是在线程内调用的,而不是在构造函数内调用的,因此调用将是虚拟的,不会在编译时解析为 A::f()

第二个问题是可以对已经被破坏的对象进行虚拟调用。 A 的析构函数,您在其中加入一个线程,在 析构 B 之后调用 。同样,对已破坏的对象进行虚拟调用是未定义的行为。

要深入了解正在发生的事情,您可以添加两个互斥量:

std::mutex m1, m2;

struct A {
    std::thread thread;
    
    A() : thread(&A::thread_f, this) { 
        std::cout << "A()" << std::endl;
    }
    
    ~A() { 
        std::cout << "~A()" << std::endl;
        thread.join();
    }

    virtual void f() = 0;
    
    void thread_f() {
        std::cout << "thread_f()" << std::endl;        
        m1.lock();
        f();
        m2.unlock();
    }
};

struct B : A {
    B() {
        std::cout << "B()" << std::endl;
        m1.unlock();
    }

    ~B() {
        m2.lock();    
        std::cout << "~B()" << std::endl;
    }

    void f() override {
        std::cout << "B::f()" << std::endl;
    }
};

int main() {
    m1.lock();
    m2.lock();
    B b;
}

现在您将看到预期的“B::f()”输出。 Demo。不过,我很难将其称为解决方案。

kenash0625 的回答解决了第一个问题,但没有解决第二个问题。