【C++】多态性与虚函数

一、同名隐藏

非虚函数
1)属性的同名隐藏
父类属性和子类属性同名,根据就近原则,访问子类属性,除非特殊说明,才能访问隐藏父对象的属性。

class Object
{
public:
	int value;
public:
	Object(int x = 0) :value(x) {}
};
class Base : public Object
{
public:
	int value;
	int num;
public:
	Base(int x = 0) :Object(x), value(x + 10), num(x + 20) {}
	void fun()
	{
		value = 10;//访问的是Base的value(就近原则) 
		num = 20;
		Object::value = 20;//访问的是Object的value
	}
};
int main()
{
	Base base;
	base.value = 10;//访问的是Base的value(就近原则) 
	base.Object::value = 20;//访问的是隐藏父对象的value
}

2)方法的同名隐藏
Base中的fun是一重载关系,但是Object和Base中的fun没有重载关系

class Object
{
public:
	void fun(int x,int y,int z) {
		cout << "Object::fun " << x << endl;
	}
};
class Base : public Object
{
public:
	void fun(int x) {
		cout << "Base::fun " << x << endl;
	}
	void fun(int a, int b) {
		cout << "Base::fun(int,int): " << a << " " << b << endl;
	}
};

int main()
{
	Base base;
	base.fun(12);//调动base自己的
	base.fun(1, 2);//调动base自己的
	//base.fun(1, 2, 3);//编译不通过,因为Base里的fun函数把隐藏基类的fun函数隐藏掉了
	base.Object::fun(1,2,3);//编译通过,告诉编译器调动的是隐藏基对象的fun函数
	return 0;
}

二、同名覆盖

虚函数

总结一:
一个类里面定义了虚函数,那么编译阶段,编译器给这个类类型产生一个唯一的vftable虚函数表,虚函数表中主要存储的内容就是RTTI指针和虚函数的地址。当程序运行时,每一张虚函数表都会加载到内存的 .rodata区

总结二:
一个类里面定义了虚函数,那么这个类定义的对象,其运行时,内存中开始部分,多存储一个vfptr虚函数指针,指向相应类型的虚函数表vftable。一个类型定义的n个对象,它们的vfptr指向的都是同一张虚函数表

总结三:
一个类里面虚函数的个数,不影响对象内存大小(vfptr),影响的是虚函数表的大小

总结四:
如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数

多态性分为两种:

1)编译时的多态 通过函数的重载和运算符的重载来实现 早期绑定
2)运行时的多态 运行时的多态是指在程序在编译时,无法根据函数名和参数来确定该调用哪一个函数,必须在程序执行过程中,根据执行的具体情况来动态确定,它是通过类继承关系public虚函数来实现的,目的也是建立一种通用的程序。通用性是程序追求的目标之一。

三、有继承关系时,成员函数应尽可能地设置为虚函数

注意
1.派生类中定义虚函数必须与基类中的虚函数同名外,还必须同参数表同返回类型。如基类中返回基类指针,派生类中返回派生类指针是允许的,这是一个例外。
在这里插入图片描述

2.只有类的成员函数才能说明为虚函数。这是因为虚函数仅适用于有继承关系的类对象。

3.实现多态性时,只能使用基类类型的指针变量引用,使该指针指向该基类的不同派生类的对象,并通过该指针指向虚函数,才能实现动态的多态性。(如果用对象+.调动,编译器在编译时就确定调动关系,不会去查表)

4.静态成员函数,是所有同一类对象共有,不受限于某个对象,不能作为虚函数。(没有this指针 不能查表)
友元 没有this指针

5.内联函数每个对象一个拷贝,无映射关系,不能作为虚函数。
(inline 编译时调用点展开代码,虚函数是运行时获得调用情况)

6.析构函数可定义为虚函数,构造函数不能定义虚函数(置虚表指针),因为在调用构造函数时对象还没有完成实例化。在基类中及其派生类中都动态分配的内存空间时,必须把析构函数定义为虚函数,实现撤消对象时的多态性(重置虚表指针)。

7.函数执行速度要稍慢一些。为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现。所以多态性总是要付出一定代价,但通用性是一个更高的目标。
8.如果定义放在类外,virtual 只能加在函数声明前面,不能(再)加在函数定义前面。


虚表在只读数据区

只能把子对象给父指针,不能把父对象给子指针(是一个)

纯虚函数

纯虚函数是指被标明为不具体实现的虚拟成员函数。它用于这样的情况:设计一个基类时,会遇到无法定义基类中虚函数的具体实现,其实现依赖于不同的派送类
virtual 返回类型 函数名(参数表) =0;“=0”表明程序员将不定义该函数,函数声明是为派生类保留一个位置。“=0”本质上是将指向函数体的指针定为NULL.

抽象类
1)含有纯虚函数的基类是不能用来定义对象的,但是可以定义指针或引用,指向它的派生类,进而实现多态性。
2)纯虚函数没有实现部分,不能产生对象,所以含有纯虚函数的类是抽象类
3)为了强调一个类是抽象类,可将该类的构造函数说明为protected
4)如果派生类没有重新定义纯虚函数,则这个派生类仍是一个抽象类。

C语言中的抽象类型void 无类型是真正意义上的泛型指针
void a; //error
void* p=nullptr; //ok 可以定义指针

在这里插入图片描述

类型层次

第一种类型:实例类型(普通类型):可以构建类型在这里插入图片描述

第二种类型:抽象类型:其中的某些方法被定义成纯虚函数
在这里插入图片描述
第三种类型:interface,(接口),不定义属性,所有的方法都定义成纯虚函数
在这里插入图片描述

联编

在联编过程中,需要确定程序中的函数调用函数的代码段之间的映射关系。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在我们创建对象时,先构建Object再构建base对象。op指针指向base的地址。Object类型的op指针,在op->fun();静态联编,确定可访问属性和默认值,公有的,编译可以通过,确定默认值10,我们调用的时候,是动态方案
我们在这里有一个base对象,base对象有隐藏的Object对象,但是虚表的指针指向的是base的虚表,查的是base的fun,没有给200给a,因为编译的时候确定的是10,所以打印Base::fun a: 10

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值