从父 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 的回答解决了第一个问题,但没有解决第二个问题。
我需要在线程成员函数中调用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 的回答解决了第一个问题,但没有解决第二个问题。