这就是我所在的方框。我想理解为什么在你的接口类中有一个虚析构函数是很重要的。如果你能坚持到最后,你就会明白为什么这些东西是用引号括起来的……我还希望所有的词汇都是绝对正确的。以下是到目前为止我在这个过程中的情况:
-
有时你有基类,有时你有继承基类的派生类。
如果你有一个发现自己指向派生对象的基指针,并且你希望从这个指向派生对象的基指针调用的成员函数表现得好像它实际上是从派生对象调用的,那么你调用的成员函数最好在基类中声明为虚函数。
接口是只有纯虚函数的类。如果从这个接口类派生一个新类并实现所有纯虚函数,那么最终可以创建派生类的实例。
你永远不能拥有接口类的实例,但是你可以拥有指向接口类的指针的实例。
如果你有pointer-to-interface-class实际上指向派生类的对象(实际上,我猜它将总是如果# 4是正确的),如果您决定删除对象通过你的指针,如果你没有一个"虚拟析构函数在接口类",你的意图摧毁派生对象只能调用摧毁基地执行对象(即接口类),由于没有虚拟析构函数,事情永远不会达到实际调用派生对象的析构函数的地步——从而导致内存泄漏。
唷。好吧,如果听起来没错,那就回答我的问题。像这样在接口内部声明一个虚析构函数是否足够:
virtual ~iFace();
那看起来不对…那么,如果像这样将析构函数设置为纯虚函数会发生什么呢?
virtual ~iFace() = 0;
既然它们只是声明,它们中的任何一个算作"接口类中的虚析构函数"吗?你甚至可以有一个已声明但未定义的析构函数吗?我猜只有纯虚拟的时候……
无论如何,回到标题的问题…我真的已经尽可能快了……这是钱的照片……如果你的"接口类中的虚析构函数"至少需要一个像这样的空定义:virtual ~iFace() {};
那么该成员函数不是纯虚的(不能因为你给了它一个定义),因此你的类不再是接口(它不只有包含纯虚成员函数)。
这意味着如果您为接口定义了虚析构函数,那么您就不再拥有接口(而只是一些抽象基类)。这只是滥用语言吗?我明白是怎么回事吗?
注:所有这些都来自于问我自己"什么是接口?",然后阅读这个问题的答案:如何在c++中声明接口?
希望这不是太长的步行,太短的车程,但我决心完全理解这些概念和相关词汇。
为什么Abstract class
析构函数应该是虚的并有定义?
在指向派生类对象的多态基类指针上调用delete
;没有虚析构函数的基类导致未定义行为。
所以你需要将多态基类的析构函数声明为virtual
。一旦显式地将析构函数声明为virtual,就需要为它提供定义。这是因为编译器默认为每个类生成(定义)析构函数,但如果您显式声明析构函数,则编译器不会这样做,而是让您为自己的析构函数提供定义。这是有意义的,因为编译器将显式声明视为您希望在析构函数中执行一些重要操作(即使您不需要这样做)的指示,并且它通过强制您给出定义来为您提供这样做的机会。
神话1:
c++中有一种叫做Interface
的东西
c++作为一种语言不提供Interface
你所说的Interface
在c++中被称为Abstract class
。Abstract Classes
是用来模拟c++中Interface
的行为。
什么是抽象类?
根据定义,抽象类应该至少有一个纯虚函数。
神话2:
抽象类中的所有函数都必须是纯虚函数
Abstract classes
并不要求其内部的所有函数都是纯虚函数。如果抽象对象至少有一个纯虚函数,则不能创建该对象。不过,正如您正确提到的,您可以创建指向它的指针。
误区3:
纯虚函数不能有定义
纯虚函数完全可以有定义。
为什么我需要一个有定义的Pure virtual function
?
代码比语言更响亮,所以这里有一个简单的例子:
警告:未编译的代码仅用于演示
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;
}
c++没有本地接口实体。接口是作为常规类实现的。
因此,在c++中使一个类成为接口的原因并不是具有普遍共识的东西。我个人认为,如果一个类没有数据成员,没有用户声明的构造函数,并且它的所有函数都是纯虚函数(析构函数可能例外),并且它的所有基类(如果有的话)也是接口,那么它就是接口。如果一个类不完全符合所有这些属性,我可能会把它称为"胖"接口(通常不是赞美!)。如果你想通过指向基类(比如"接口"类)的指针删除动态分配的多态类,那么基类析构函数必须声明为virtual
。这意味着它必须是用户声明的析构函数,而不是隐式声明的非virtual
的析构函数。
一旦显式声明了析构函数,就必须为它提供实现。(无论基类析构函数是否声明为纯虚、虚或非虚,当您销毁从它派生的任何类的实例时,都将始终使用基类析构函数。)这纯粹是c++语言实现的细节。这并不意味着你的基类不像一个"接口",如果你有一个接口类,那么析构函数的实现很可能在任何情况下都是空的——你没有成员或有成员的基类需要担心。
如果你的接口至少有一些纯虚函数,那么将析构函数标记为纯并没有真正的好处,你的接口类已经是一个抽象类了。派生类析构函数在技术上不重写基类析构函数,因此您不需要派生类提供用户声明的析构函数或类似的东西。
将析构函数声明为纯虚函数还剥夺了在类定义中内联提供析构函数定义的能力,尽管这是一个次要细节。
"接口是任何只有纯虚函数的类"
——c++中的概念叫做抽象类。抽象类是具有至少一个纯虚函数的类。但是,它并不要求它的所有成员函数都是纯虚函数。你不能实例化任何抽象类。
"这意味着如果你为你的接口,那么您就不再拥有接口(但只有一些)抽象基类)。这只是滥用语言吗?我明白发生了什么吗?"
——相反,必须为析构函数提供定义,即使它是纯虚的,因为析构函数在继承层次结构中总是以自下而上的方式调用。
标准12.4:
析构函数可以声明为虚函数(10.3)或纯虚函数(10.4);如果在程序中创建了该类的任何对象或任何派生类,则应定义析构函数。
例子:
class A
{
public:
// this is stil a pure virtual function
// when there is a definition
virtual ~A() = 0;
};
class B: public A
{};
int main()
{
// fail to link due to missing definition of A::~A()
B b;
}
- 。
- 好;如果成员函数没有在基类中声明为虚函数,则调用基类中的成员函数;如果成员函数在基类中既没有定义也没有声明为纯虚函数,则会出现错误。 在c++中,你不像在Java和c#中那样有接口;c++中的抽象基类结合了后两种语言中的接口和抽象类。如果一个c++类至少有一个纯虚成员函数,那么它就是抽象的。
- 用抽象类替换接口。
- 如果基类的析构函数没有声明为虚函数,那么从基类的指针中删除派生类会发生什么?
考虑到所有这些,通常你的抽象基类已经有了一些纯虚成员函数,确保它不可能被实例化,所以通常的做法是定义一个不做任何事情的内联虚析构函数。