删除期间的C ++类型更改
我已经读过,当你构造一个派生类型时,类型会根据被调用的构造函数而改变。因此,如果您创建派生对象并使用基指针调用虚函数,通常它将映射到派生类中的实现。如果你在基类构造函数中调用虚函数,它将使用基类实现,因为对象的类型在技术上是在该函数中的基类类型。例如(临时代码,抱歉,如果它不编译):
class Base {
Base()
{
std::cerr << "Base Constructor.";
func();
}
virtual void func() {
std::cerr << "Func base called." << std::endl;
}
};
class Derived : public Base {
Derived()
{
std::cerr << "Derived Constructor.";
func();
}
void func() {
std::cerr << "Func derived called." << std::endl;
}
};
int main() {
Derived* d = new Derived;
delete d;
}
应输出:
Base Constructor.
Func base called.
Derived Constructor.
Func derived called.
首先,这是真的还是依赖于实现?
如果我使用RTTI和typeinfo,基数中打印的类型实际上是基数类型,还是更多的是一种不成文的规则呢?
考虑到这一点,从构造函数调用虚函数是危险的,或者只要你知道你在做什么就安全吗?
答案 0 :(得分:8)
为了简单起见,您可以制定规则:
在构造函数和析构函数中禁用虚拟机制
Base类中的虚函数调用将始终调用函数的基类版本,派生类中的相同函数会导致调用函数的Derived类版本。
首先,这是真的还是依赖于实现?
是的,这总是如此。这不依赖于实现。
如果我使用RTTI和typeinfo,那么打印在基础中的类型实际上是基数类型吗?
是的,它将是Base;当您在Base类构造函数中时,派生对象甚至不存在。
考虑到这一点,从构造函数调用虚函数是危险的,或者只要你知道你在做什么就安全吗?
只要您理解其背后的语义,从构造函数调用虚函数就没有危险。
This C++ FAQ 应该是一本很好的读物。
答案 1 :(得分:2)
它定义明确。
[n3290: 12.7/4]:
会员功能,包括虚拟功能 (10.3),可在施工或毁坏期间调用(12.6.2)。 直接或间接从虚拟函数调用时 构造函数或来自析构函数,包括在构造期间或 破坏类的非静态数据成员,以及对象 调用适用的是正在构建的对象(称为x
)或 破坏,被调用的函数是最终的覆盖者 构造函数或析构函数的类,而不是覆盖它的一个 更多派生类。如果虚函数调用使用显式 类成员访问(5.2.5)和对象表达式引用x
的完整对象或该对象的基类子对象之一但是 <{1}}或其基类子对象之一,行为未定义。
答案 2 :(得分:1)
斯科特迈耶斯有一篇很好的文章。这是他的书“Effective C ++”。 该文章可在以下网址找到: Never Call Virtual Functions during Construction or Destruction
它还讨论了另一种实现方式。
最近我遇到了类似的问题,我这样解决了:
class EthernetFrame
{
protected:
/** ctor to be called from derived classes */
EthernetFrame(unsigned inPayloadLength)
{
calculatePadBytes(inPayloadLength);
}
private:
/** calculates needed required PadBytes for Frames < 64B
* @param inPayloadLength we need to know the length of the actual L3 frame
*/
void calculatePadBytes(unsigned inPayloadLength);
};
class IPv4Frame : public EthernetFrame
{
public:
/** create empty IPv4 packet */
IPv4Frame() :
EthernetFrame(cIPv4_MINIMUM_LENGTH)
{};
// IPv4 header + trailer in bytes
unsigned cIPv4_MINIMUM_LENGTH;
protected:
/** ctor to be called from derived classes */
IPv4Frame(unsigned inPayloadLength) :
EthernetFrame(cIPv4_MINIMUM_LENGTH+inPayloadLength)
{};
};