您的位置:首页 > 编程语言 > C语言/C++

C++中虚函数、纯虚函数、抽象类、重载、覆盖、隐藏的区别与联系

2017-04-23 09:59 459 查看
虚函数:被virtual关键字修饰的成员函数,就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。

指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。

纯虚函数:基类只是一个界面函数,其实现由派生类来完成,如果没有将所有的基类纯虚函数实现(没有写基类的纯虚函数会继承,那么派生类仍然是抽象类),那么这个派生类仍然还是抽象类(不能够说明其对象)。

class A

{

public:

virtual void func() = 0;

{

// 此处可写纯虚函数的代码,其调用方式只能是在成员函数中通过作用域运算符调用

}

void test()

{

A::func(); // 不管静态联编还是动态联编只会调用类A中的func()

func(); // 动态联编

}

};

class B: public A

{

public:

virtual void func() // 如果没有写这个函数,那么就会继承基类中的这个方法,因此本类 仍然是抽象类

{

// 实现代码

}

};

抽象类:只要含有任何一个纯虚函数的类,不能够说明抽象类的对象。可以看出,对于纯虚函数明显比虚函数要优越,因为它不能够说明抽象类对象(纯虚函数实现会在派生类中,派生类没有实现,会依次迭代下去,直到实现为止),这样就会实现运行时的多态性。

虚函数表

类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址(类中所有的虚函数的地址)。

编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中分配了指向这个表的指针的内存(也就是说类(存在虚函数或纯虚函数)中的每一个实例对象的第一个隐藏的数据成员就是指向这个虚函数表的指针变量,占4个字节)。编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

另一个问题就是,对象的占用内存空间(存在虚函数第一个4个字节是存放虚函数表的指针变量,其他空间就是类中声明的数据成员)的计算,其计算方式与结构体中所有变量占用空间类似,都是采用一种内存对齐的规则。

a. 成员函数被重载的特征:(某一个类的对象根据参数列表来作为函数调用依据,这是在编译时完成)

(1)相同的范围(在同一个类中)

(2)函数名相同

(3)参数不同(有不同的签名:类型,个数,顺序)

(4)virtual关键字可有可无

b. 隐藏:派生类的函数隐藏了与其同名的基类函数,也可说为支配规则,派生类函数支配基类中的同名函数(对象从属于哪个类就会调用哪个类中成员函数,这是在编译时完成)

(1)如果派生类与基类中的函数同名,但是参数不同。此时不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数中没有声明关键字virtual.此时,基类中的函数同样被隐藏(注意别与覆盖混淆)

c. 覆盖:派生类函数覆盖其基类函数(来源于哪个类的对象,就会调用哪个类的成员函数,这是在运行时完成的)

(1)不同的范围(派生类与基类)

(2)函数名相同

(3)参数相同

(4)基类函数必须含有关键字virtual

注意,如果试图仅仅通过函数的返回值来区别函数(在函数名、参数都相同的情况下),将会出现错误
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息