对C ++中的非纯虚拟类感到困惑,它们有什么用?

时间:2013-03-15 08:12:39

标签: c++ hyperlink virtual vtable

我有一个声明如下的类:

class TestFoo {
public:
    TestFoo();
    virtual void virtualFunction();
    void nonVirtualFunction();
};

我尝试以这种方式实现

TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}

在编译时返回错误:

undefined reference to vtable for TestFoo

我试过了:

TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}
void TestFoo::virtualFunction(){}

编译确定与这些帖子的答案一致:

Undefined reference to vtable

undefined reference to vtable

令我困惑的是,我认为声明虚函数的重点在于我不需要定义它。在这个例子中,我不打算创建任何TestFoo实例,而是创建从TestFoo继承的(具体)类的实例。但是,我仍然想为TestFoo的每个子类定义函数nonVirtualFunction。

我做错了什么?

谢谢!

4 个答案:

答案 0 :(得分:7)

  

声明虚函数的重点是我不会   需要定义它

不完全,它说“我可能想用派生类中的其他东西替换此函数的实现。”

我可能误解了你的问题,但你似乎暗示你不认为你可以在C ++中定义纯虚拟成员函数,你可以。您可以声明,如下所示。

virtual void virtualFunction() = 0;

通常,纯虚函数不会定义,但当然可以。这说“这个函数没有默认实现,因为它并不总是有意义,但我会为你提供一个你可以选择的实现。”

顺便说一下,如果一个类有任何虚拟函数,你还应该定义一个虚拟析构函数,因为它是完全合法的(并且经常被推荐)指向派生类的基类(智能)指针 - 没有虚拟析构函数,该对象可能无法正确delete

答案 1 :(得分:3)

  

......我想到了宣告一个问题的重点   虚函数是我不需要定义它...

对于该设施,您有一个名为纯虚拟方法的功能:

virtual void virtualFunction() = 0;  // no linking error now

请注意,virtual方法无法保持未实现状态。原因是对于virtual主体内的每个class方法声明,必须有一个vtable条目。未能找到其正文会导致链接错误。

此限制的目的
除非一个类是抽象的 - 也就是说它至少有一个虚函数 - 否则你无法向编译器保证你不会声明TestFoo的对象。当您执行以下操作时会发生什么:

DerivedOfTestFoo obj1;
TestFoo obj2 = obj1, *p = &obj2; // object slicing
p->virtualFunction(); // where is the body?

其他情况;在构造函数中没有virtual机制:

TestFoo::TestFoo () {
  this->virtualFunction(); // where is the body?
}

我们可以得出结论,编译器遵循“更安全而不是抱歉”的规则。 :)

答案 2 :(得分:1)

您的描述与抽象类的情况完全匹配。将您的虚函数声明为:

    virtual void VirtualFunction () = 0;

这意味着您没有在此类中实现该功能。结果,班级变得抽象。也就是说,不能实例化此类的裸对象。

此外,您应该提供虚拟析构函数。

更新:一些澄清......

该语言允许您重新定义非虚拟功能。但是,在某些情况下可能会调用错误的版本:

derived D;    // rB is a reference to base class but it
base & rB=D;  // points to an object of the derived class

rB.NonVirtualFunction ();  // The base-class version is called

出于这个原因,现在强烈建议不要重新定义非虚函数。请参阅Scott Meyers的“Effective C ++,第三版:55改进程序和设计的具体方法”,第36项:“永远不要重新定义继承的非虚函数。”

另见第7项:“在多态基类中声明析构函数虚拟”。一个例子:

base * pB = new derived;
delete pB;                // If base's destructor is not virtual,
                          // ~derived() will not be called.

如果您想知道为什么默认情况下并非所有内容都是虚拟的,原因是调用虚函数比调用非虚函数稍慢。哦,具有虚函数的类的对象每个占用几个字节。

答案 3 :(得分:0)

如果你想把这个虚函数作为纯虚函数,那么不要定义它,virtual void virtualFunction()= 0;