多态和虚函数

函数多态就相当于函数的重载。
在调用函数时,一个函数名可以接受不同的参数列表,执行不同的代码,所以具有多态性。

动态多态:
程序运行时的多态,主要是就 对象 而言的。但C++中实现多态不能直接用对象,而是用指向对象的指针或引用

将基类指针转换为派生类指针就是向下转换,如:

Drerived *D=static_cast<Base*>(*B);

当基类中的函数不能满足派生类的特殊性,就可以考虑使用动态多态。

如何实现动态多态:

1.与多态行为相关的函数,在基类中声明为虚函数。
2.派生类改写基类中的虚函数。
3.通过基类类型的指针(或引用)调用虚函数。

派生类重新声明虚函数时:

1.函数名相同
2.参数列表相同
3.与基类中的const或非const成员函数一致
4.返回值类型相同,或者是存在继承关系类型的指针,即派生类虚函数返回指针的类型继承自基类返回指针的类型。

举例,B,C是正确的虚函数声明。

A.Base*Base::copy(Base*)
  Base*Derived::copy(Derived*)//不满足2
B.Base*Base::copy(Base*)
  Derived*Derived::copy(Base*)
C.ostream& Base::print(int,ostream&=cout)//调用默认实参是由指针类型决定的。
  ostream& Derived::print(int,ostream&)
D.void Base::eval()const
  void Derived ::eval()//不满足3

派生类重新声明虚函数时,virtual关键字可加可不加。一个成员函数只要在基类里声明为虚函数,则在其后所有层次的派生类中都是虚函数。

如果基类指针调用一个非虚函数,而该函数又调用了一个虚函数。则实际被调用的也是基类指针所指对象的虚函数。指向子类对象就调用的是子类对象的虚函数。

当使用类的指针调用成员函数时,普通函数由指针类型决定,而虚函数由指针指向的实际类型决定 虚函数调用实例

纯虚函数

virtual <返回类型> function(<参数列表>)=0;

纯虚函数首先是虚函数。如果某个虚函数不需要在基类中实现,或者实现也没有实际意义,则应该声明为纯虚函数。

有纯虚函数的类是一个抽象类,是不能实例化对象的。
如:

class Shape{
	shape(){}
	~shape(){}
	virtual void Draw()=0;
};
int main(){
	Shape s;//错误,Shape是一个抽象类,是不能实例化对象的。
	return 0
}

派生类必须改写基类中所有的纯虚函数,否则还是一个抽象类。

虚函数表

一个类中,如果定义了虚函数,那就会分配一个虚函数表和虚函数表指针。发生继承时,派生类也会创建虚函数表。

虚析构函数
基类指针指向派生类对象,普通析构只会调用基类析构函数。如果基类声明了虚析构函数,那析构的时候就会从派生类开始调用析构函数,直到基类的析构函数。

举例:

#include<iostream>
#include<cstdlib>
#include<vector>
using namespace std;
struct Base{
	virtual void move() = 0;//纯虚函数。
	virtual ~Base(){
		cout << "delete" << endl;
	}//虚析构函数
};
class Truck :public Base{
public:
	/*virtual*/void move(){//纯虚函数重写
		cout << "Truck move" << endl;
	}
};
class Boat :public Base{
	void move(){
		cout << "Boat move" << endl;
	}
};
class Plane :public Base{
	void move(){
		cout << "Plane move" << endl;
	}
};
int main(){
	vector<Base*>base;//创建指向基类的指针数组
	base.push_back(new Truck());
	base.push_back(new Boat());
	base.push_back(new Plane());
	vector<Base*>::iterator iter = base.begin();
	while (iter != base.end()){
		(*iter)->move();
		iter++;
	}
	iter = base.begin();
	while (iter != base.end()){
		delete (*iter);
		iter++;
	}
	system("pause");
	return 0;
}

输出:
Truck move
Boat move
Plane move
delete
delete
delete

虚函数的sizeof问题:
包含虚函数的类中,是有一个指向虚函数表的指针。

sizeof(虚函数)=sizeof(虚函数指针)=4;

虚继承
参考:
虚拟继承与sizeof

虚继承的时候,是有一个指向父类的虚类指针。

普通继承就是直接把父类的sizeof大小+子类新添的内容。

虚继承还有加一个指向父类的虚类指针的大小。

不同的编译器虚继承时,虚表指针共享方法是不一样的。
GCC不管是不是虚继承,虚表指针都是共享的;

VC虚继承时,虚表函数是不共享的。非虚继承时,虚表指针是共享的(就是自己类里也有虚函数,算不算自己这个虚函数指针的问题)
如果虚继承的时候,虚函数覆盖了。那虚表指针只能算一个。

#include<iostream>
#include<assert.h>
#include<memory.h>
#include<vector>
using namespace std;
class A{
	char k[3];
public:
	virtual void aa(){};
};
class B:public virtual A{
	char j[3];
public:
	virtual void aa(){};//virtual void bb(){};如果换成bb(),则输出 8,20,32
};
class C :public virtual B{
	char i[3];
public:
	virtual void cc(){};
};
int main(){
	cout << "sizeof(a)" << sizeof(A) << endl;//8
	cout << "sizeof(b)" << sizeof(B) << endl;//16
	cout << "sizeof(C)" << sizeof(C) << endl;//28
	system("pause");
	return 0;
}

还有一种是两个类没有继承关系的
具体问题详见:C++多态的一个例子

  1. 其中,A* p = (A*)&object;它主要就是为了能让A类的指针指向object的地址,因为A和B类不是继承关系,所以如果不加(A*)是不能指过去的,VS里会报错。
  2. p->f2() 为什么输出的是 B::f1
    找到f2函数在A的虚函数表中的位置,
    然后找到B里这个位置上的虚函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值