2011-09-19 40 views
1

C++型变化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,打印在底座上的类型实际上是底座的类型,还是更多的是不成文的规则类型?

从构造函数中调用虚拟函数是否有危险,或者只要知道自己在做什么,它是否安全?

回答

8

为了保持短期和简单的,你可以有一个规则:

虚机制在构造函数和析构函数

禁止在基类中的虚函数调用总是会调用基类版本的函数,派生类中的相同结果是调用函数的派生类版本。

首先,这是否始终如此?还是与实施有关?

是的,这是永远正确的。这不是实现相关的。

如果我使用了RTTI和typeinfo,那么打印在底座上的类型实际上是底座的类型吗?

是的,它会是基地;当您处于Base类构造函数中时,派生对象甚至不存在。

想到这一点,从构造函数中调用虚函数是危险的,或者只要知道自己在做什么,它是否安全?

不,从构造函数调用虚函数并不危险,只要你理解它后面的语义。


This C++ FAQ应该是你一个很好看的。

+0

“为了简化简单”和错误的。 –

2

它是明确的。

[n3290: 12.7/4]:成员函数,包括虚拟函数 (10.3),可构造或破坏(12.6.2)中被调用。 当虚拟函数是从 构造或从析构函数直接或间接地称为,包括建筑或 破坏类的非静态数据成员的过程中,将对象到 该呼叫应用的是在建的对象(称为x)或 销毁,被调用的函数是 构造函数或析构函数类中的最终覆盖,而不是一个覆盖更多派生类的 类。如果虚拟函数调用使用显式的 类成员访问权限(5.2.5)并且对象表达式引用x的完整对象或该对象的基类子对象之一,但使用 而不是x或其基类子对象之一,行为是不确定的。

1

有一篇来自Scott Meyers的优秀文章。它来自他的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) 
    {}; 

}; 
+0

我不明白你的片段与多态函数调用的相关性。 –

+0

代码片段不涉及关于函数的多态性。这是一个从链接文章派生出来的实现,适用于我自己的问题。 –

+0

其实它很贴切,这就是为什么我为它+1了。我在阅读了那本书的那部分内容之后问了这个问题(它是什么,第20项?) - 我只是想进一步问一些后续问题。他的片段是Myers提出的解决方案的一个很好的应用。无论如何,我赞赏真实世界的样本:) –