26

所以这就是我所在的盒子。我想了解为什么在你的接口类中有一个“虚拟析构函数”很重要。如果你能坚持到最后,你就会明白为什么这些东西是用引号引起来的……我也想让所有的词汇都绝对正确。到目前为止,这是我在这个过程中所处的位置:

  1. 有时你有基类,有时你有从基类继承的派生类。

  2. 如果您有一个发现自己指向派生对象的基指针,并且您希望从该基指针指向派生对象的成员函数调用表现得好像它实际上已被调用从派生对象,那么你调用的成员函数最好在基类中声明为virtual。

  3. 接口是任何只有纯虚函数的类。如果从这个接口类派生出一个新的类,并且实现了所有的纯虚函数,那么最终就可以创建派生类的实例了。

  4. 你永远不可能有一个接口类的实例,但是你可以有一个指向接口类的实例。

  5. 如果你有一个指向接口类的指针,它实际上指向派生类的一个对象(实际上,我想如果#4 是正确的,它总是必须如此),并且如果你决定通过删除该对象您的指针,那么如果您没有“接口类中的虚拟析构函数”,那么您销毁派生对象的意图将仅作为销毁基对象(即接口类)的调用执行,因为没有虚拟析构函数,事情永远不会到达实际调用派生对象的析构函数的地步——从而导致内存泄漏。

呸。好的,如果这听起来不错,请回答我的问题。像这样在你的界面中声明一个虚拟析构函数就足够了:

virtual ~iFace();

这在我看来是错误的......所以如果你让析构函数像这样纯虚拟会发生什么:

virtual ~iFace() = 0;

由于它们只是声明,因此其中任何一个都算作“接口类中的虚拟析构函数”吗?你甚至可以有一个已声明但未定义的析构函数吗?只有当它是纯虚拟的我猜...

无论如何,所以回到标题问题......我真的尽可能快......这是钱......如果你的“你的接口类中的虚拟析构函数”至少需要一个像这样的空定义:

virtual ~iFace() {};

那么该成员函数不是纯虚函数(不可能是因为您给了它一个定义),因此您的类不再是接口(它不仅包含纯虚成员函数)。

这意味着如果你为你的接口定义了一个虚拟析构函数,那么你就不再有一个接口(而只是一些抽象基类)。这只是语言的滥用吗?我明白发生了什么吗?

注意:所有这一切都来自于问自己“什么是接口?” 然后阅读这个问题的答案: How do you declare an interface in C++?

希望步行时间不会太长,但我决心完全理解这些概念及其相关词汇。

4

4 回答 4

28

为什么Abstract class析构函数应该是虚拟的并且有定义?

调用delete指向派生类对象的多态基类指针和没有虚拟析构函数的基类会导致未定义的行为

因此,您确实需要将多态基类的析构函数声明为virtual. 一旦你明确声明你的析构函数是虚拟的,你就需要为它提供一个定义。这是因为编译器默认为每个类生成(定义)一个析构函数,但如果您显式声明析构函数,则编译器不会这样做,而是让您为自己的析构函数提供定义。这是有道理的,因为编译器将显式声明视为您想要在析构函数中执行一些非平凡操作(即使您不需要这样做)的指示,并且它通过强制您给您提供这样做的机会定义。


误解 1:C++ 中
有一个叫做 an 的东西。Interface

NO
C++ 作为一种语言不提供Interface 你所指的在 C++Interface中称为的。用于模拟C++ 中的行为。Abstract classAbstract ClassesInterface

什么是抽象类?
根据定义,一个抽象类应该至少有一个纯虚函数。


误区二:
Abstract 类中的所有函数都必须是纯虚函数。

NO
Abstract classes不要求它们内部的所有函数都是纯虚拟的。如果抽象对象至少具有一个纯虚函数,则不能创建该对象。不过,正如您正确提到的,您可以创建指向它的指针。


误区三:
纯虚函数不能有定义。

NO
纯虚函数有一个定义是完全有效的。


为什么我需要一个Pure virtual functionwith 定义?
代码比语言更响亮,所以这里是一个简单的例子:
警告:未编译的代码仅用于演示

class IMyInterface
{
    int i;
    int j;
    public:
        virtual void SetMembers(int ii, int jj)=0;
};

/*The pure virtual function cannot be inline in the class definition*/
/*So this has to be here*/
void IMyInterface::SetMembers(int ii, int jj)
{
    i = ii;
    j = jj;
}

class Myclass: public IMyInterface
{
    int k;
    int l;
    public:
        virtual void SetMembers(int ll, int m, int a, int b)
        {
             k = ll;
             l = m;
             IMyInterface::SetMembers(a,b);
         }
};

int main()
{

    MyClass obj;
    obj.SetMembers(10,20,30,40);
    return 0;
}
于 2011-08-06T13:14:36.200 回答
10

C++ 没有本机接口实体。接口被实现为常规类。

因此,使类成为 C++ 中的接口的原因并不是具有普遍协议的东西。我个人认为一个类是一个接口,如果它没有数据成员,没有用户声明的构造函数,并且它的所有函数都是纯虚拟的 - 除了它的析构函数可能例外 - 并且它的所有基类,如果有的话,也是接口。如果一个类不能完全符合所有这些属性,我可能会将其称为“胖”接口(通常不是恭维!)。

如果要通过指向基类(例如“接口”类)的指针删除动态分配的多态类,则必须声明基类析构函数virtual。这意味着它必须是用户声明的析构函数,而不是隐式声明的非virtual.

一旦显式声明了析构函数,就必须为其提供实现。(无论基类析构函数是否声明为纯虚拟、虚拟或非虚拟,当您销毁从它派生的任何类的实例时,将始终使用基类析构函数。)这纯粹是 C++ 语言的实现细节。这并不意味着您的基类不再是“接口”,如果您有一个接口类,那么析构函数的实现很可能在任何情况下都是空的 - 您没有成员或基类会员担心。

如果你的接口至少有一些纯虚函数,那么将析构函数标记为纯的没有真正的好处,你的接口类已经是一个抽象类。派生类析构函数在技术上不会覆盖基类析构函数,因此您不需要派生类提供用户声明的析构函数或类似的东西。

将析构函数声明为纯虚函数也会剥夺您在类定义中提供内联析构函数定义的能力,尽管这是一个小细节。

于 2011-08-06T13:27:43.157 回答
7
于 2011-08-06T13:18:18.787 回答
2
  1. 好的。
  2. 好的; 如果成员函数未在基类中声明为 virtual,则调用基类中的成员函数;如果成员函数既没有在基类中定义也没有声明为纯虚函数,则会出现错误。
  3. 在 C++ 中,您没有 Java 和 C# 中的接口;C++ 中的抽象基类结合了接口和抽象类,因为它们存在于后两种语言中。如果一个 C++ 类至少有一个纯虚成员函数,那么它就是抽象类。
  4. 抽象类替换接口
  5. 如果基类的析构函数未声明为虚拟,则从指向基类的指针中删除派生类时,正式地您无法假设会发生什么。

考虑到这一切,通常你的抽象基类已经有一些纯虚成员函数来确保它不可能实例化它,所以通常的做事方式是定义一个什么都不做的内联虚析构函数。

于 2011-08-06T13:31:18.967 回答