C++多态(虚函数,纯虚函数,抽象类)

一.多态

1.理解:

多种形态,多种形式

eg:多个派生类均把基类的方法run重新实现,但是实现的方式不同,体现了多种形式,即为多态

2.分类

(1)编译时的多态:在编译过程中确定了同名操作的具体操作对象;

(2)运行时的多态:程序运行过程中才动态地确定操作所针对的对象;

3.联编

是确定操作的具体对象的过程------把一个标识符名和存储地址联系在一起的过程;

联编:把一条消息和一个对象的方法结合的过程;

根据进行的截断的不同,可以分为:静态联编和动态联编;

静态联编------编译时多态性------函数重载和运算符重载

动态链表------运行时多态性------虚函数

二.函数重载

函数的重载也称多态函数:使得程序能用同一个名字来访问一组相关的函数,提高程序的灵活性

函数名相同,但是函数所带的参数的个数或者数据类型不同;------编译器根据参数决定调用哪个

1.形式:(1)参数个数或类型有所差别;(2)参数完全相同但属于不同的类

主要谈参数完全相同,但是属于不同的类;

方法:用对象名区别或者用类名和范围解析运算符区别

eg:Point pob(15,15)

pob.area() Point::area()

三.虚函数

虚函数实现的是动态的重载;函数调用与函数体之间的联系是在运行时建立的;动态联编

1.定义虚函数

在基类中进行,把基类中需要定义为虚函数的成员函数声明为virtual

基类中的某个成员函数被声明为虚函数后,就可以在派生类中重新定义。在派生类中重新定义时,其函数原型包括返回类型,函数名,参数个数和类型,参数的顺序都必须与基类中的原型完全一致

(指向派生类对象的指针,不能指向私有派生类的对象;当指向公有派生类的对象是只能直接访问派生类中从基类继承下来的成员不能直接访问公有派生类中定义的成员;)

注意事项:

(1)虚函数的声明只能出现在类函数原型的声明中,不能出现在函数实体实现时;

(2)基类中只有保护成员或公有成员才能被声明为虚函数;

(3)在派生类中重新定义虚函数时,关键字virtual可写可不写

(4)动态联编只能通过成员函数来调用或通过指针,引用来访问虚函数

四。多级继承和虚函数

多级继承的虚函数与单继承的虚函数的调用相同。不同类创建的对象调用的函数是不一样的;

cpp 复制代码
class Base
{
public:
	virtual void func()
	{
		cout << "Base output" << endl;
	}
};

class Derived1 :public Base
{
public:
	void func()
	{
		cout << "Derived output!" << endl;
	}
};

class Derived2 :public Derived1
{
public:
	void func()
	{
		cout << "Derived2 output!" << endl;
	}
};

void test(Base& b)
{
	b.func();
}

int main()
{
	Base bObj;
	Derived1 d1Obj;
	Derived2 d20bj;//当把基类中的virtual去掉之后,再次运行程序,此时的结果为:只是Base output
	test(bObj);
	test(d1Obj);
	test(d20bj);

}

五.纯虚函数和抽象类

抽象类:包含纯虚函数的特殊的类;

建立抽象类是为了多态的使用抽象类的成员函数;

1.纯虚函数

在当前的基类中不能为虚函数给出一个有意义的实现时,可将其声明为纯虚函数;纯虚函数的实现留给派生类来完成,纯虚函数的作用是为派生类提供一个一致的接口;

一般来说:一个抽象类至少有一个纯虚函数;

纯虚函数的定义:

virtual<函数类型><函数名>(参数表)=0;

virtual void set()=0;//在这里并不表示返回值为0,只是起到形式上的作用,告诉编译器它是纯虚函数,说明在基类中不用定义该函数的函数体;

2.抽象类

包含纯虚函数的一种特殊的类,是为了抽象和设计而建立的;并且抽象类是不能创建对象的, 为了强调一个类是抽象类,可以将该类的构造函数声明为保护的访问控制权限;

只做子类的共同的操作接口;

抽象类只能用做其他类的基类,不能创建抽象类的对象;不能用作参数类型,函数的返回类型或显式转换的类型;

cpp 复制代码
struct IDraw
{
	virtual void draw() = 0;
};

struct ISize
{
	virtual void getSize(int& w, int& h) = 0;
};

class Shape :public ISize, public IDraw
{

};

class Circle :public Shape
{
private:
	int m_x, m_y, m_r;
public:
	Circle(int x, int y, int r);
	void draw();
	void getSize(int& w, int& h);
};

Circle::Circle(int x, int y, int r) :m_x(x), m_y(y), m_r(r)
{
	;
}
void Circle::draw()
{
	cout << "Draw a circle" << endl;
}

void Circle::getSize(int& w, int& h)
{
	w = 2 * m_r;
	h = 2 * m_r;
}

int main()
{
	Circle circle(0, 0, 123);
	IDraw* pDraw = &circle;
	pDraw->draw();
	ISize* pSize = &circle;
	int w = 0, h = 0;
	pSize->getSize(w, h);
	cout << "Width:" <<w<< endl;
	cout << "Height:" << h << endl;
	return 0;
}

通过上述例子:我们可以声明抽象类的对象指针或者引用,上述:IDraw*pDraw;就可以通过这个指针或者引用来访问派生类的对象成员

(在这里说明:C++中,结构体和类几乎是相同的,主要区别在默认的访问权限,类默认为私有的,而结构体默认为公有的,除了上述区别之外,;类可以继承自结构体,结构体也可以继承自类或者其他结构体,并且结构体中,我们经常定义接口(一组相关的纯虚函数),类可以从这些接口结构体继承,以实现这些接口,就比如上述:IDraw中的draw就为纯虚函数)


相关推荐
hccee8 分钟前
C# IO文件操作
开发语言·c#
hummhumm13 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
hunandede19 分钟前
av_image_get_buffer_size 和 av_image_fill_arrays
c++
J老熊23 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
zmd-zk37 分钟前
flink学习(2)——wordcount案例
大数据·开发语言·学习·flink
好奇的菜鸟41 分钟前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript
手握风云-1 小时前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构
喵叔哟1 小时前
重构代码中引入外部方法和引入本地扩展的区别
java·开发语言·重构