C++ 如何修复称为运行时错误的纯虚函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14549489/
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
How to fix pure virtual function called runtime error?
提问by Andrew
I understand why I am getting the error I am getting (pure virtual function called). I am trying to call pure virtual functions from within the destructor of my base class shown below. However, I do not know how to rework my code to prevent this from happening. Here are the base and derived classes (the relevant portions anyway):
我明白为什么我得到我得到的错误(调用纯虚函数)。我正在尝试从如下所示的基类的析构函数中调用纯虚函数。但是,我不知道如何重新编写代码以防止这种情况发生。以下是基类和派生类(相关部分无论如何):
Base class:
基类:
TailFileManager::TailFileManager(const std::string &filename, const int fileOpenPeriod_ms)
: m_Stop(false)
{
m_WorkerThread.reset(new boost::thread(boost::bind(&TailFileManager::TailFile, this, filename, fileOpenPeriod_ms)));
}
TailFileManager::~TailFileManager()
{
m_Stop = true;
m_WorkerThread->join();
}
void TailFileManager::TailFile(const std::string &filename, const int fileOpenPeriod_ms)
{
std::ifstream ifs(filename.c_str());
while (! ifs.is_open())
{
boost::this_thread::sleep(boost::posix_time::milliseconds(fileOpenPeriod_ms));
ifs.open(filename.c_str());
}
ifs.seekg(0, std::ios::end);
while (! m_Stop)
{
ifs.clear();
std::string line;
while (std::getline(ifs, line))
{
OnLineAdded(line);
}
OnEndOfFile();
}
ifs.close();
}
Derived class:
派生类:
ETSLogTailFileManager::ETSLogTailFileManager(const std::string &filename, const int heartbeatPeriod_ms)
: TailFileManager(filename, heartbeatPeriod_ms),
m_HeartbeatPeriod_ms(heartbeatPeriod_ms),
m_FoundInboundMessage(false),
m_TimeOfLastActivity(0)
{
}
ETSLogTailFileManager::~ETSLogTailFileManager()
{
}
void ETSLogTailFileManager::OnLineAdded(const std::string &line)
{
// do stuff...
}
void ETSLogTailFileManager::OnEndOfFile()
{
// do stuff...
}
回答by Saqlain
You shouldn't call virtual functions during construction or destruction, because the calls won't do what you think, and if they did, you'd still be unhappy. If you're a recovering Java or C# programmer, pay close attention to this Item, because this is a place where those languages zig, while C++ zags.
你不应该在构造或销毁过程中调用虚函数,因为调用不会按照你的想法去做,如果他们做了,你仍然会不高兴。如果你是一个正在恢复的 Java 或 C# 程序员,请密切关注这个 Item,因为这是那些语言曲折而 C++ 曲折的地方。
Re-work your design i.e you may call some cleanup function before object get destroyed, idea is just avoid virtual function during const/dest (if there are any!), if you are working with C++...
重新设计你的设计,即你可以在对象被销毁之前调用一些清理函数,想法只是在 const/dest 期间避免虚函数(如果有的话!),如果你正在使用 C++...
The rules for virtual invocation are different. C++ 2003, section 12.7 "Construction and Destruction", says:
虚拟调用的规则是不同的。C++ 2003,第 12.7 节“构造和破坏”,说:
Lets refresh some old memories ...
让我们重温一些旧的回忆......
Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructoras own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructoras class, or overriding it in one of the other base classes of the most derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructoras own class or one of its bases, the result of the call is undefined.
成员函数,包括虚函数 (10.3),可以在构造或销毁 (12.6.2) 期间调用。当从构造函数(包括从数据成员的 mem 初始化器)或从析构函数直接或间接调用虚函数时,并且该调用适用的对象是正在构造或销毁的对象,则调用的函数是在构造函数或析构函数自己的类或其基类之一中定义的函数,但不是在从构造函数或析构函数类派生的类中覆盖它的函数,或在最派生对象的其他基类之一中覆盖它(1.8 )。如果虚函数调用使用显式类成员访问(5.2.
Because of this difference in behavior, it is recommended that you never invoke an object's virtual function while it is being constructed or destroyed.
由于这种行为差异,建议您在构造或销毁对象时不要调用对象的虚函数。
Never Call Virtual Functions during Construction or Destruction An Excerpt from Effective C++, Third Edition by Scott MeyersJune 6, 2005
切勿在构造或销毁过程中调用虚拟函数 摘自 Effective C++,Scott Meyers 第三版2005 年 6 月 6 日
回答by Matthieu M.
As far as the C++ standard is concerned:
就 C++ 标准而言:
- if you call a virtual function in a constructor or destructor, then the function is dynamically dispatched as if its dynamic type were that of the current constructor/destructor being executed (§12.7/4)
- if that function happened to a pure virtual, then this is undefined behavior(§10.4/6); the Itanium ABIdefines the behavior:
__cxa_pure_virtual
is called.
- 如果您在构造函数或析构函数中调用虚函数,则该函数将被动态调度,就好像其动态类型是正在执行的当前构造函数/析构函数的类型一样(第 12.7/4 节)
- 如果该函数发生在纯虚拟上,则这是未定义的行为(第 10.4/6 节);在安腾ABI定义的行为:
__cxa_pure_virtual
被调用。
So, you have a bit of a thorny issue...
所以,你有一个棘手的问题......
A possible solution to the problem would be to break it down in two parts, in order to break the destruction in two parts. This could be achieved with a Strategy
pattern:
该问题的一个可能解决方案是将其分解为两部分,以便将破坏分为两部分。这可以通过一个Strategy
模式来实现:
- provide a customizable interface, your strategy
- provide a manager class that encapsulate the functionality and defers to the strategy for the customizable parts
- 提供可定制的界面,您的策略
- 提供一个管理器类来封装功能并遵循可定制部件的策略
Let's make it clearer:
让我们说得更清楚:
class Interface {
public:
friend class Manager;
private:
virtual void finalize() = 0;
}; // class Interface
class Manager {
public:
explicit Manager(std::unique_ptr<Interface>&&);
~Manager();
private:
std::unique_ptr<Interface> _interface;
}; // class Manager
Manager::~Manager() {
_interface->finalize();
}
The trick ? At the point where finalize()
is called the destruction of _interface
has not begun yet! The call to the destructor will happen later; and thus you do not suffer from a half-dead object's fate.
诀窍?到了finalize()
所谓的毁灭_interface
还没有开始的地步!对析构函数的调用将在稍后发生;因此你不会遭受半死不活的对象的命运。
I'll end this answer by a warning about join
-ing a thread in a destructor now. Beware that destructors are automatically called in case of stack unwinding, it might therefore be dangerous to wait indefinitely while failing; especially if the thread is waiting for data that should be provided by the currently being unwoundone... a classic case of dead-lock.
我将通过一个关于join
-ing 析构函数中的线程的警告来结束这个答案。请注意,在堆栈展开的情况下会自动调用析构函数,因此在失败时无限期等待可能是危险的;特别是如果线程正在等待当前正在解开的线程应该提供的数据......死锁的经典案例。
References(n3337):
参考文献(n3337):
§12.7/4Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class.
§10.4/6Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.
§12.7/4成员函数,包括虚函数 (10.3),可以在构造或销毁 (12.6.2) 期间调用。当从构造函数或析构函数直接或间接调用虚函数时,包括在类的非静态数据成员的构造或析构期间,并且该调用适用的对象是正在构造的对象(称为 x)或销毁,被调用的函数是构造函数或析构函数类中的最终覆盖程序,而不是在派生程度更高的类中覆盖它。
§10.4/6可以从抽象类的构造函数(或析构函数)调用成员函数;对于从这样的构造函数(或析构函数)创建(或销毁)的对象,直接或间接对纯虚函数进行虚调用(10.3)的效果是未定义的。
回答by Cheers and hth. - Alf
You write,
你写,
“I am trying to call pure virtual functions from within the destructor of my base class shown below.”
“我试图从我的基类的析构函数中调用纯虚函数,如下所示。”
And the code in question is
有问题的代码是
TailFileManager::~TailFileManager()
{
m_Stop = true;
m_WorkerThread->join();
}
Happily in a single-threaded execution this couldn't possibly call a pure virtual function. But the thread that you're join
ing might call a pure virtual function on this object, possibly via a non-virtual member function. If so, then the issue is with the threading, specifically the lifetime management of this object.
令人高兴的是,在单线程执行中,这不可能调用纯虚函数。但是您正在join
调用的线程可能会通过非虚成员函数调用此对象上的纯虚函数。如果是这样,那么问题在于线程,特别是该对象的生命周期管理。
Unfortunately you do not show the relevant code. Try to reduce things to a small, complete, working example. Where "working" in the sense that it reproduces the problem.
不幸的是,您没有显示相关代码。尝试将事情简化为一个小的、完整的、有效的例子。在“工作”的意义上,它重现了问题。