c++---多态和虚函数

一、多态

 c++有三大特性:封装,继承,多态
多态是面向对象程序设计的一个重要特征,多态就是一个东西有多重状态,具有不同功能的函数可以用一个函数名,这样就可以用一个函数名实现不同的功能
静态多态和动态多态静态多态是利用重载实现的,在程序编译时确定要调用的是哪个函数,也称为编译时多态。动态多态是利用虚函数实现的,在程序执行期间才动态的确定操作所指对的对象,也称为运行时多态
动态多态:当一个基类被继承为不同的派生类时,各派生类可以使用与基类成员相同的成员名,如果在运行时用同一个成员名调用类对象的成员,会调用哪个?通过继承而产生了相关的不同派生类,与基类成员同名的成员在不同的派生类中有不同的含义

二、虚函数

 在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数类型相同的函数,这时系统会根据同名覆盖的原则决定调用的对象
 如果你要调用同名的父类中的函数的话,必须在调用使用时注明类域。虚函数的作用就是允许在派生类中重新定义与基类同名的函数,并可以通过基类指针或引用来访问基类和派生类中同名函数

在父类中声明display函数时,在左端加上**关键字virtual**,这样就将父类中的display函数声明为虚函数

class Father
{
public:
	vitrual void display()
	{
		cout<<_father<<endl;
	}
};
class Son : public Father
{
public:
	void display()
	{
		cout<<_son<<endl;
	}
};
int main()
{
	Father f;
	Son s;
	Father* p=&f;//指向基类对象
	p->display();//调用基类中函数
	p=&s;//指向派生类对象
	p->display();//调用派生类中函数
	return 0;
}

 基类的指针是用来指向基类对象的,如果用它来指向派生类的对象,则进行指针类型的转化,即基类指针指向的是派生类中从基类中继承的部分。声明为虚函数之后,在派生类中的虚函数取代了基类原来的虚函数,因此在使用基类指针指向派生类对象后,调用虚函数就是调用了派生类的虚函数

注意问题

  • 在基类中用virtual声明成员函数是虚函数,类外定义时不需要再加virtual
  • 当一个成员函数被声明为虚函数后,在派生类中的同名函数都是虚函数,如果在派生类中没有同名的函数时,那么派生类只是简单的继承基类的虚函数
  • 定义一个指向基类对象的指针变量,想要调用谁的虚函数,就让指针指向同一类中需要调用该函数的对象
  • 函数重载是在一个类中,虚函数是在基类与派生类之间的,还与重载不同的是,虚函数要求函数的首部完全相同
  • 因为虚函数用在类的继承层次的,所以只能将类的成员函数声明为虚函数
  • 如果是通过指向基类对象的指针调用派生类中成员函数,则应声明为虚函数
  • 一般将析构函数生命为虚函数,就算基类不需要析构函数,也要显示的定义一个函数体为空放入虚析构函数,以保证在释放对象动态分配空间时能得到正确的处理

纯虚函数

 在类中将某一成员函数声明为虚函数,只是因为派生类的需求,在基类中预留一个函数名,具体功能留给派生类定义,这时可以将基类中的虚函数声明为纯虚函数

virtual void fun(int x,int y)=0;

纯虚函数没有函数体,这种形式就是告诉编译器,是一个纯虚函数,留在派生类中定义。
 含有纯虚函数的类就成为抽象类。抽象类只是一种基本的数据类型,根据需求在派生类定义各种功能。这个类的作用就是为派生类提供一个公共接口,抽象类不能定义对象,但是可以定义指向抽象类的指向变量。

虚函数表

 当父类的指针或引用指向父类对象时调用的是父类虚函数,当指向子类对象时调用的是子类的虚函数,这是通过虚函数表实现的,虚函数表是通过一块连续内存来存储虚函数的地址
 使用虚函数,系统会有一定的空间开销,当一个类中有虚函数时,编译系统会为该类构造一个虚函数表,是一个指针数组,用来存放每个虚函数入口地址,虚函数表的最后一个元素是一个空指针
在这里插入图片描述
每一个虚函数表都是一个函数指针数组,而每一个虚函数表都由一个虚函数表指针维护,这个虚函数表指针叫做_vptr。

单继承模式

class Father
{
public:
 Father()
  :_f(1)
 {}
 virtual void fun1()
 {}
 virtual void fun2()
 {}
private:
 int _f;
};
class Son:public Father
{
public:
 Son()
  :_s(2)
 {}
 virtual void fun1()
 {}
 virtual void fun3()
 {}
 virtual void fun4()
 {}private :
 int _s;
};
typedef void(*FUNC)();
void PrintVTable(int *VTable)
{
 cout << "虚函数表地址" << VTable << endl;
 for (int i = 0; VTable[i] != 0; ++i)
 {
  cout << i << ":" << VTable[i]<<endl;
  FUNC f = (FUNC)VTable[i];
  f();
 }
 cout << endl;
}
int main()
{
 Son a;
 PrintVTable((int *)(*(int*)(&a)));
 system("pause");
 return 0;
}

在这里插入图片描述
虚函数表实质为函数指针数组,里面放的是函数指针,指向函数

多继承模式

class A
{
public:
	A()
		:_a(1)
	{}
	virtual void fun1()
	{
		cout<<"A::fun1()"<<endl;
	}
	virtual void fun2()
	{
		cout<<"A::fun2()"<<endl;
	}
private:
	int _a;
};
class B
{
public:
	B()
		:_b(2)
	{}
	virtual void fun3()
	{
		cout<<"B::fun3()"<<endl;
	}
	virtual void fun4()
	{
		cout<<"B::fun4()"<<endl;
	}
private:
	int _b;
};
class C:public A,public B
{
public:
	C()
		:_c(3)
	{}
	virtual void fun1()
	{
		cout<<"C::func1()"<<endl;
	}
	virtual void fun3()
	{
		cout<<"C::fun3()"<<endl;
	]
	virtual void fun5()
	{
		cout<<"C::fun5()"<<endl;
	}
private:
	int _c;
};
typedef void(*FUNC) ();
void PrintVTable(int *VTable)
{
	cout<<"虚函数表地址"<<VTable<<endl;
	for(int i=0;VTable[i]!=0;++i)
	{
		cout<<i<<":"<<VTable[i]<<"         “;
		FUNC f = (FUNC)VTable[i];
		f();
	}
	cout<<endl;
}
int main()
{
	C c;
	PrintVTable((int *)(*(int *)(&c)));
	PrintVTable((int *)(*((int *)(&c)+sizeof(A)/4)));
	system("pause");
	return 0;
]

在这里插入图片描述
其中fun5()函数是在C中声明为虚函数的,但是并不为C单独建立虚函数表,而是把fun5()函数的地址写到C中的第一个虚函数表的最后

菱形继承模型

class A
{
public:
	A()
		:_a(1)
	{}
	virtual void fun1()
	{
		cout<<"A::fun1()"<<endl;
	}
	virtual void fun2()
	{
		cout<<"A::fun2()"<<endl;
	}
private:
	int _a;
};
class B:public A
{
public:
	B()
		:_b(2)
	{}
	virtual void fun1()
	{
		cout<<"B::fun1()"<<endl;
	}
	virtual void fun3()
	{
		cout<<"B::fun3()"<<endl;
	}
private:
	int _b;
};
class C:public A
{
public:
	C()
		:_c(3)
	{}
	virtual void fun1()
	{
		cout<<"C::fun1()"<<endl;
	}
	virtual void fun3()
	{
		cout<<"C::fun3()"<<endl;
	}
private:
	int  _c;
};
class D:public B,public A
{
public:
	D()
		:_d(3)
	{}
	virtual void fun1()
	{
		cout<<"C::fun1()"<<endl;
	}
	virtual void fun4()
	{
		cout<<"C::fun4()"<<endl;
	}
private:
	int _d;
};
int main()
{
	D d;
	PrintVTable((int *)(*(int *)(&d)));
	system("pause");
	return 0;
}

在这里插入图片描述
菱形多重继承的对象模型中,先是B中的虚函数表指针,再是B从A继承的成员变量,最后是B的成员变量。接下来是C中的虚函数表指针,最后是C的成员变量。最后是D的成员变量。D中的虚函数fun4()的地址则存放到了第一个虚函数表的最后

在这里插入图片描述
由于fun2()函数一直没有被重写,所以还是A的函数,而fun1()函数最终被D中的fun1()函数重载,所以虚函数表中的fun1函数被D中的虚函数fun1()地址覆盖

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值