C++中的多态与静态 《从C语言过渡到C++和虚幻引擎中的C++编程》教程③

前言:

多态按字面的意思就是多种形态。

同一个名称的方法,在不同的实例上可以有不同的不同表现形式或形态,这便是多态。在C++中,我们可以通过重写父类中的方法实现多态,虚函数的重写在C++的多态中也扮演着重要角色。

多态

一、重写(overwirte)

在C++中,子类可以继承父类的代码,在此基础上重写(overwirte)某些父类的方法来实现与父类有所不同的功能。

重写的方法需要和被重写的方法函数名相同,参数类型和返回值类型也要相同。

例如:

输出结果为:

除了成员方法(又称成员函数)可以重写外,成员变量也可以被重写。

但我们要知道,子类的成员变量重写后其实会新建一个名称相同变量,在子类的方法和外部的代码中只能访问到这个新建的变量,但父类中原有的被重写的变量会依然存在,仍然可以通过其父类的方法访问和修改它,但子类中的方法和外部的代码只能访问到新建的那个变量。

扫描二维码关注公众号,回复: 14893112 查看本文章

举个例:

输出结果为:

同时我们还发现,C++构造子类的实例时,会先调用其父类的构造方法,再调用其自己的构造方法

补充内容:重写和重载的区别:

重载(注意:读chong 载,不是zhong 载,英文名overload)

是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。

我们可以通过重载,让同一个名称的成员方法拥有多种不同参数列表,在使用时,可以根据参数的不同调用不同方法,来完成同样的功能。

类的构造函数也可以被重载,使用时可以根据参数类型的不同采用不同的构造方法,但析构函数不能,因为析构函数不能带有任何形式参数。

重载的使用例子:

重载的方法之间是并列或者说水平的关系,而重写是在覆盖父类中的方法,重写和被重写的方法之间是垂直的关系。

二、虚函数和抽象类

虚函数

在C++中,虚函数是指拥有virtual关键字前缀的函数。虚函数在子类被重写后可以通过其父类类型的指针访问到其子类中重写的方法。

假设,我们要开发一个游戏,

里面有法师和剑士两种角色,

并且都具有血量(float blood)、法力值(float mana)两种受保护的属性。

法师攻击时会消耗10点法力值,剑士攻击时不消耗法力值;

法师受到伤害时,会受到完全的伤害,剑士受伤时,因为剑士自带铠甲可以防御掉20%的伤害。

法师的攻击力为100,剑士的攻击为80,两者的血量和法力值都为100。

于是我们的代码可以这样写:

输出结果:

如果我们去掉上面代码中的virtual关键字,输出结果就会变成:

可见,在这里,父类指针Character* c只访问到了父类中的getHurt()方法去接受伤害,所以剑士被扣了100血,并没有按照我们重写的getHurt()去接受伤害,但我们给父类的getHurt()方法添加virtual关键字后,父类指针Character* c才能访问到子类重写出来的getHurt()方法,才能实现剑士可以减少20%的伤害的效果。

纯虚函数

如果在虚函数的末尾增加=0,那么它就会成为纯虚函数

纯虚函数具有以下特点:

(1)纯虚函数没有函数体;

(2)最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是虚函数”;

(3)这是一个声明语句,最后有分号。

(4)拥有至少一个纯虚函数的类叫做抽象类,抽象类自身不能被直接实例化,但它的子类在重写父类中 的纯虚函数方法后就能该子类就能实例化。

抽象类

在软件开发中,我们发现有些对象具有一些相同的方法,但这种方法无法脱离该对象而单独存在,比如圆和正方形,他们都属于几何图形,我们要计算他们的面积,那么编程时中就得为他们提供一个计算面积的方法作为获取其面积的接口,外部的代码就可以提供这个接口获取这个图形的面积。而这个接口本身可以算作一类事物,但它肯定是不能脱离图形类的实例而单独存在的(或者说能被实例化的)。

所以C++中为我们提供了抽象类(又称接口类)的功能,让我们能专门写这样的接口类。

不用定义对象而只作为一种基本类型用作继承的类叫做抽象类(也叫接口类),凡是包含纯虚函数的类都是抽象类,抽象类的作用是作为一个类族的共同基类,为一个类族提供公共接口,抽象类不能实例化出对象。

纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

那么在C++中我们就可以这样写:

输出结果:

override关键字

override关键字是C++11标准中新增的帮助你重写父类中的虚函数的语法糖。

在没有override关键字时,如果你重写的方法与父类方法的参数、名称、属性不一样,编译器并不会报错,而是会把它当成新的函数,程序就会违背你的意愿,如下:

如果你要保证你正确重写了父类中的方法,你可以在你重写的方法后加上override关键字,编译器就会检查你写的重写的方法的形式参数表、类型、属性是否和父类方法相同,如果不同就会报错提醒你去改正

如果你要重写出纯虚函数,则=0要添加到override关键字后,如下:

final关键字

final关键字也是C++11中新增的语法糖帮助你防止父类方法被重写,也可以防止父类被继承。

如果你在写父类的方法时,你希望子类不能重写修改这个方法,那么你可以为其添加final关键字,那么在子类重写这个方法时,编译器就会报错,如下:

如果你把final关键字写在父类类名后面,那么这个父类就不能被继承,如下:

静态

静态成员变量

在C++中,对象的内存包含了其成员变量,不同的对象,占用的内存区域是不同的,这使得不同对象的成员变量相互独立,它们的值不受其他对象的影响。例如有两个相同类型的对象 a、b,它们都有一个成员变量 name_,那么修改 a.name_ 的值不会影响 b.name_ 的值。

但是我们在实际编程的时候,有的时候希望某一类的对象拥有一些共享的数据,而不是每个对象的实例都存一份数据。

所以在C++,提供了静态成员变量来实现多个对象共享数据的目标。静态成员变量是一种特殊的成员变量,它被关键字static修饰,例如:

输出结果:

static 成员变量属于该类所有对象共有的,不属于某个具体的对象,即使创建多个对象,也只分配一份内存,静态成员变量在这个类的第一个实例创建后,其在内存空间中的位置是固定或者说静态不变的(也就是静态存储),该类的所有实例使用的都会是这份内存中的数据。

所以上面的代码,我们修改静态成员变量a[1].b=5后,a[2].b也会等于5,因为它们都使用的同一段内存区域。

静态方法(又称静态成员函数)

如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。

静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。

静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。

静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。

例如:

输出结果:

猜你喜欢

转载自blog.csdn.net/lifesize/article/details/128582539