这安全吗?
这安全吗?
class Derived: public PublicBase, private PrivateBase
{
...
~Derived()
{
FunctionCall();
}
virtual void FunctionCall()
{
PrivateBase::FunctionCall();
}
}
class PublicBase
{
virtual ~PublicBase(){};
virtual void FunctionCall() = 0;
}
class PrivateBase
{
virtual ~PrivateBase(){};
virtual void FunctionCall()
{
....
}
}
PublicBase* ptrBase = new Derived();
delete ptrBase;
此代码使用IP在错误的地址中折叠有时。
每个人都知道在构造函数上调用虚函数不是一个好主意。
从类似http://www.artima.com/cppsource/nevercall.html的文章中,我知道析构函数也不是调用虚函数的好地方。
我的问题是“这是真的吗?”我已经用VS2010和VS2005测试过,并且调用了PrivateBase :: FunctionCall。是未定义的行为吗?
我将在这里违背流程...但是首先,我必须假定您的PublicBase
析构函数是虚拟的,否则将永远不会调用Derived
析构函数。
从构造函数/析构函数调用虚拟函数通常不是一个好主意。]
原因是在这两个操作期间动态调度很奇怪。在构造过程中对象changes
的实际类型,在销毁过程中再次为对象changes。当执行析构函数时,对象正是该类型,而不是从其派生的类型。动态调度始终有效,但是虚拟函数的final Overrover会根据您在层次结构中的位置而变化。也就是说,永远不要期望以任何从执行的构造函数/析构函数的类型派生的类型执行对构造函数/析构函数中的虚函数的调用。
但是
在您的特定情况下,final
替代程序(至少对于层次结构的此部分而言)是您的级别。而且,您完全是不使用动态分配。调用PrivateBase::FunctionCall();
是静态解析的,实际上等效于对任何非虚拟函数的调用。函数是否为virtual的事实并不影响此调用。所以是
可以按照您的意愿做,尽管大多数人都会学习规则的原则而不是原因,但是您将不得不在代码审查中解释这一点。这安全吗?
是。从构造函数或析构函数调用虚拟函数会分派该函数,就像该对象的动态类型是当前正在构造或销毁的那样。在这种情况下,它是从Derived
的析构函数调用的,因此它被分派到Derived::FunctionCall
(在您的情况下,它是非虚拟地调用PrivateBase::FunctionCall
的)。所有这些都定义明确。
出于以下三个原因,从构造函数或析构函数调用虚拟函数不是一个好主意:
这是我为了帮助自己更好地了解破坏过程而编写并运行的,您可能也会发现有帮助
#include <iostream>
using namespace std;
class A {
public:
virtual void method() {
cout << "A::method" << endl;
}
void otherMethod() {
method();
}
virtual ~A() {
cout << "A::destructor" << endl;
otherMethod();
}
};
class B : public A {
public:
virtual void method() {
cout << "B::method" << endl;
}
virtual ~B() {
cout << "B::destructor" << endl;
}
};
int main() {
A* a = new B();
a->method();
delete a;
}
这安全吗?是和否
是的,因为您的原样示例定义明确,可以正常工作。所有这些都可以通过其他答案很好地解释。另外,此代码是完全安全的,因为它不会以编写的方式进行编译:基类中的私有dtor等。
这样做的原因是不安全的,因为此代码假定其他人都不会覆盖您的
FunctionCall
类中的Derived
。and
不会期望在销毁对象时调用该覆盖。 。最有可能的编译器会对此抱怨。您可以通过将FunctionCall
标记为final来改进代码:class Derived : public PublicBase, private PrivateBase { ... virtual void FunctionCall() final; }
或您的Derived
类为final:
class Derived final : public PublicBase, private PrivateBase { ... virtual void FunctionCall(); }
如果您在较旧的编译器上进行编译,或者由于任何其他原因而无法使用c++11,那么您至少可以在此处更加明确,并在代码中准确地写出运行时将发生的情况,无论FunctionCall
是否被以下任何子类覆盖您的Derived
类:
class Derived : public PublicBase, private PrivateBase { ... ~Derived() { Derived::FunctionCall(); // be explicit which FunctionCall will be called } virtual void FunctionCall() { PrivateBase::FunctionCall(); } }
这安全吗?
这安全吗?