C++ 调用纯虚方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2167335/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pure Virtual Method Called
提问by jakogut
EDIT: SOLVED
编辑:已解决
I'm working on a multi-threaded project right now where I have a base worker class, with varying worker classes that inherit from it. At runtime, the worker classes become threads, which then perform work as needed.
我现在正在做一个多线程项目,我有一个基本的工作类,从它继承的不同的工作类。在运行时,工作类变成线程,然后根据需要执行工作。
Now, I have a Director I've written which is supposed to maintain an array of pointers to all of the workers, so that it can retrieve information from them, as well as modify variables within them later.
现在,我编写了一个 Director,它应该维护一个指向所有 worker 的指针数组,以便它可以从它们中检索信息,并在以后修改它们中的变量。
I did this by creating a pointer to a pointer of the base class:
我通过创建一个指向基类指针的指针来做到这一点:
baseWorkerClass** workerPtrArray;
Then in the constructor of the Director, I dynamically allocate an array of pointers to the base worker class:
然后在 Director 的构造函数中,我动态分配一个指向基本工作类的指针数组:
workerPtrArray = new baseWorkerClass*[numWorkers];
In the constructor of each worker thread, the worker calls a function in the director which is meant to store the pointer of that worker in the array.
在每个工作线程的构造函数中,工作线程调用控制器中的一个函数,该函数旨在将该工作线程的指针存储在数组中。
Here's how the director stores the pointers:
以下是导演存储指针的方式:
Director::manageWorker(baseWorkerClass* worker)
{
workerPtrArray[worker->getThreadID()] = worker;
}
Here is an example of a worker variant. Each worker inherits from the base worker class, and the base worker class contains pure virtual functions which should exist in all worker variants, as well as a few variables which are shared between all workers.
这是一个工人变体的例子。每个worker都继承自基础worker类,基础worker类包含应该存在于所有worker变体中的纯虚函数,以及一些在所有worker之间共享的变量。
class workerVariant : protected baseWorkerClass
{
public:
workerVariant(int id)
: id(id)
{
Director::manageWorker(this);
}
~workerVariant()
{
}
int getThreadID()
{
return id;
}
int getSomeVariable()
{
return someVariable;
}
protected:
int id;
int someVariable
};
Then the baseWorkerClass looks something like this:
然后 baseWorkerClass 看起来像这样:
class baseWorkerClass
{
public:
baseWorkerClass()
{
}
~baseWorkerClass()
{
}
virtual int getThreadID() = 0;
virtual int getSomeVariable() = 0;
};
After each worker variant is done initializing, I should end up with an array of pointers to baseWorkerClass objects. This means I should be able to, for example, get the value of a given variable in a certain worker using its ID as the index to the array, like so:
在每个工作变量完成初始化之后,我应该得到一个指向 baseWorkerClass 对象的指针数组。这意味着我应该能够,例如,使用某个工人的 ID 作为数组的索引来获取给定变量的值,如下所示:
workerPtrArray[5]->getSomeVariable(); // Get someVariable from worker thread 5
The problem is that this code causes a crash in a Windows executable, without any explanation of why, and in Linux, it says:
问题是这段代码导致 Windows 可执行文件崩溃,没有任何解释原因,在 Linux 中,它说:
pure virtual method called
terminate called without an active exception
Aborted
被
调用的纯虚方法被调用而没有活动异常
中止
I could've sworn I had this working at some point, so I'm confused as to what I've screwed up.
我可以发誓我在某个时候有这个工作,所以我对我搞砸的事情感到困惑。
Actual unmodified code that has the problem:
存在问题的实际未修改代码:
Worker variant header: http://pastebin.com/f4bb055c8
Worker variant source file: http://pastebin.com/f25c9e9e3
Worker 变体标头:http: //pastebin.com/f4bb055c8
Worker 变体源文件:http: //pastebin.com/f25c9e9e3
Base worker class header: http://pastebin.com/f2effac5
Base worker class source file: http://pastebin.com/f3506095b
基本工作类标题:http: //pastebin.com/f2effac5
基本工作类源文件:http: //pastebin.com/f3506095b
Director header: http://pastebin.com/f6ab1767a
Director source file: http://pastebin.com/f5f460aae
导演头
文件:http: //pastebin.com/f6ab1767a
导演源文件:http: //pastebin.com/f5f460aae
EDIT: Extra information, in the manageWorker function, I can call any of the pure virtual functions from the pointer "worker," and it works just fine. Outside of the manageWorker function, when I try to use the pointer array, it fails.
编辑:额外信息,在 manageWorker 函数中,我可以从指针“worker”调用任何纯虚函数,它工作得很好。在 manageWorker 函数之外,当我尝试使用指针数组时,它失败了。
EDIT: Now that I think about it, the thread's entry point is operator(). The Director thread is created before the workers, which may mean that the overloaded parenthesis operator is calling pure virtual functions before they can be overridden by the child classes. I'm looking into this.
编辑:现在我考虑了一下,线程的入口点是 operator()。Director 线程是在工作线程之前创建的,这可能意味着重载的括号运算符在它们可以被子类覆盖之前调用纯虚函数。我正在研究这个。
回答by John Feminella
The problem appears to be that Director::manageWorker
is called in the constructor of workerVariant
instances:
问题似乎Director::manageWorker
是在workerVariant
实例的构造函数中调用的:
Director::manageWorker(baseWorkerClass* worker) {
workerPtrArray[worker->getThreadID()] = worker;
}
Presumably getThreadID()
isn't a pure virtual function or you would have (hopefully!) gotten a compiler error about not overriding it in workerVariant
. But getThreadID()
might call other functions which you should override, but are being invoked in the abstract class. You should double check the definition of getThreadID()
to make sure you're not doing anything untoward that would depend on the child class before it's properly initialized.
大概getThreadID()
不是纯虚函数,否则你会(希望如此!)得到一个编译器错误,关于不在workerVariant
. 但getThreadID()
可能会调用您应该覆盖的其他函数,但在抽象类中被调用。您应该仔细检查 的定义,getThreadID()
以确保在正确初始化之前没有做任何取决于子类的不良行为。
A better solution might be to separate this sort of multi-stage initialization into a separate method, or to design Director
and baseWorkerClass
such that they don't have this sort of initialization-time interdependency.
更好的解决方案可能是将这种多阶段初始化分离到一个单独的方法中,或者进行设计Director
,baseWorkerClass
使它们没有这种初始化时间的相互依赖性。
回答by Igor Zevaka
Without seeing the full code I would hazard a guess that you are stepping out of the boundary of the memory block pointed to by workerPtrArray
. It would certainly make sense since it complains about pure virtual function being called. If the memory being dereferenced is garbage then the runtime can't make sense of it at all and weird things happen.
在没有看到完整代码的情况下,我会冒险猜测您正在走出 指向的内存块的边界workerPtrArray
。这当然是有道理的,因为它抱怨调用了纯虚函数。如果被取消引用的内存是垃圾,那么运行时根本无法理解它,并且会发生奇怪的事情。
Try to put asserts into critical places where you are dereferencing the array to make sure that indices make sense. I.e. limit to 4 workers and make sure the id is less than 4.
尝试将断言放在要取消引用数组的关键位置,以确保索引有意义。即限制为 4 个工人并确保 id 小于 4。
回答by Chris Becke
During initialization classes are only partially constructed. Specifically, constructors have to be executed from the most ancestor class up, so that each derived class'es constructor can safely access its base members.
在初始化期间,类只是部分构造。具体来说,构造函数必须从最祖先类向上执行,以便每个派生类的构造函数可以安全地访问其基成员。
this means that the vtable of a partially constructed class can not be in its final state - if virtual methods were allowed to call derived classes, those methods would be called before that classes constructor had been called.
这意味着部分构造的类的 vtable 不能处于其最终状态 - 如果允许虚拟方法调用派生类,则将在调用该类构造函数之前调用这些方法。
Which means that, during construction, pure virtual functions are in fact, pure virtual. Modern c++ compilers are getting better at catching this - but its possible to in many cases "bury" the illegal call in such a way that the compiler doesn't notice the error.
这意味着,在构造过程中,纯虚函数实际上是纯虚函数。现代 C++ 编译器在捕捉这个方面做得越来越好 - 但在许多情况下它可能以编译器不会注意到错误的方式“掩埋”非法调用。
Moral of the story: dont do anything in your constructor that will invoke a virtual function. It just won't do what you expect. Even when it isn't pure.
这个故事的寓意是:不要在构造函数中做任何会调用虚函数的事情。它只是不会做你期望的。即使它不纯。
回答by AProgrammer
Pure virtual function called means that a member which is pure in the base class is called before the execution of the constructor of the descendant is started. In non multi-thread programs, this means directly or indirectly in the constructor of the base class. In a multi-threaded program, this can also happen when the constructor launch the thread in the constructor and the system execute the thread before having terminated the constructor.
调用纯虚函数是指在子类的构造函数开始执行之前调用基类中纯的成员。在非多线程程序中,这意味着直接或间接在基类的构造函数中。在多线程程序中,当构造函数在构造函数中启动线程并且系统在终止构造函数之前执行线程时,也会发生这种情况。
回答by David Smith
I didn't see the variant class being constructed in any of your code samples. Are you sure the id being passed is within range for the worker array? Also, you're constructing the objects using 'new', right? If you constructed the object on the stack, it would register itself with the Director, but after the constructor returns the object will be immediately destroyed, but the Director will retain it's pointer to the object that was on the stack.
我没有看到在您的任何代码示例中构造了变体类。您确定传递的 id 在工作器数组的范围内吗?此外,您正在使用“新”构建对象,对吗?如果您在堆栈上构造对象,它将向 Director 注册自己,但是在构造函数返回后,该对象将立即被销毁,但 Director 将保留它指向堆栈上的对象的指针。
Also, your baseWorkerClass destructor should be virtual along with the workerVariant destructor to make sure they get called when you delete the array of baseWorkerClass.
此外,您的 baseWorkerClass 析构函数应该与 workerVariant 析构函数一起是虚拟的,以确保在您删除 baseWorkerClass 数组时调用它们。
From my comment to another question, consider using std::vector instead of the double pointer. It's easier to maintain and understand and removes your need to maintain the array.
从我对另一个问题的评论,考虑使用 std::vector 而不是双指针。维护和理解更容易,并且不需要维护阵列。
It seems like you've added an unnecessary layer of abstraction here. I don't think the id should really be part of the subclass interface. I think something like this might work better for you:
您似乎在这里添加了一个不必要的抽象层。我认为 id 不应该真正成为子类接口的一部分。我认为这样的事情可能对你更有效:
class baseWorkerClass
{
public:
baseWorkerClass(int id) :
id( id )
{
}
virtual ~baseWorkerClass()
{
}
int getThreadID(){ return id; };
virtual int getSomeVariable() = 0;
protected:
int id;
};
class workerVariant : protected baseWorkerClass
{
public:
workerVariant(int id) :
baseWorkerClass( id )
{
Director::manageWorker(this);
}
virtual ~workerVariant()
{
}
int getSomeVariable()
{
return someVariable;
}
protected:
int someVariable
};
回答by shojtsy
Aren't you by any chance accessing the objects after they are destructed? Because during destruction the vtable pointers are gradually "rolled back" so that vtable entries will point to methods of the base class, some of which are abstract. After deleting the object, the memory could be left as it was during the destructor of the base class.
在它们被破坏后,您是否有机会访问这些对象?因为在销毁期间 vtable 指针会逐渐“回滚”,因此 vtable 条目将指向基类的方法,其中一些是抽象的。删除对象后,内存可以保留在基类的析构函数期间。
I suggest you try memory debugging tools, such as valgrindor MALLOC_CHECK_=2. Also on unix it is quite easy to get a stacktrace for such fatal errors. Just run your application under gdb, or TotalView, and at the point the error happens it will automatically stop, and you can look at the stack.
我建议您尝试使用内存调试工具,例如valgrind或MALLOC_CHECK_=2。同样在 unix 上,很容易获得此类致命错误的堆栈跟踪。只需在 gdb 或TotalView下运行您的应用程序,在发生错误时它会自动停止,您可以查看堆栈。
回答by sirbrialliance
I got this error message once, and though it doesn't relate to the asker's exact case, I add this in hopes that it might be useful to others:
我曾经收到过此错误消息,尽管它与提问者的具体情况无关,但我添加了此信息,希望对其他人有用:
I fixed the issue by doing a clean build.
我通过干净的构建解决了这个问题。