1

我有一个声明如下的类:

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(){}

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

对 vtable 的未定义引用

对 vtable 的未定义引用

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

我做错了什么?

谢谢 !

4

4 回答 4

7

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

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

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

virtual void virtualFunction() = 0;

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

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

于 2013-03-15T08:16:48.173 回答
3

...我认为声明虚函数的全部意义在于我不需要定义它...

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

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

请注意,virtual方法不能保持未实现。原因是对于在主体中声明virtual的每个方法都必须有一个条目。未能找到它的主体会导致链接错误。classvtable

这个限制的目的
除非一个类是抽象的——也就是说它至少有一个虚函数——否则你无法向编译器保证你不会声明一个对象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?
}

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

于 2013-03-15T08:21:31.077 回答
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.

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

于 2013-03-15T08:25:24.030 回答
0

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

于 2013-03-15T08:21:55.227 回答