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就为纯虚函数)


相关推荐
西猫雷婶5 分钟前
python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶
开发语言·python·opencv
kiiila6 分钟前
【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
开发语言·qt
小_太_阳31 分钟前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾34 分钟前
scala借阅图书保存记录(三)
开发语言·后端·scala
唐 城1 小时前
curl 放弃对 Hyper Rust HTTP 后端的支持
开发语言·http·rust
码银3 小时前
【python】银行客户流失预测预处理部分,独热编码·标签编码·数据离散化处理·数据筛选·数据分割
开发语言·python
从善若水3 小时前
【2024】Merry Christmas!一起用Rust绘制一颗圣诞树吧
开发语言·后端·rust
lqqjuly3 小时前
特殊的“Undefined Reference xxx“编译错误
c语言·c++
冰红茶兑滴水3 小时前
云备份项目--工具类编写
linux·c++
刘好念4 小时前
[OpenGL]使用 Compute Shader 实现矩阵点乘
c++·计算机图形学·opengl·glsl