C++中的多态
原创
©著作权归作者所有:来自51CTO博客作者CurryCoder的原创作品,请联系作者获取转载授权,否则将追究法律责任
1.Cpp中的多态
- 多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
- 多态的实例如下:
#include "iostream"
using namespace std;
class Shape{
protected:
int width;
int height;
public:
Shape(int a = 0, int b = 0): width(a), height(b){
cout << "基类Shape类的构造函数\n";
}
int area(){
cout << "基类Shape的area(): " << endl;
return 0;
}
};
class Rect: public Shape{
public:
Rect(int a = 0, int b = 0): Shape(a,b){
cout << "派生类Rect的构造函数\n";
}
int area(){
cout << "派生类Rect的area(): " << endl;
return width * height;
}
};
class Triangle: public Shape{
public:
Triangle(int a=0, int b=0): Shape(a, b){
cout << "派生类Triangle的构造函数\n";
}
int area(){
cout << "派生类Triangle的area(): " << endl;
return width * height / 2;
}
};
int main(){
Shape *shape; // 指向类Shape的指针shape
Rect r(10, 7);
Triangle t(10, 5);
shape = &r; // 存储矩形的地址
// 调用矩形的求面积函数
shape->area();
// 存储三角形的地址
shape = &t;
// 调用三角形的求面积函数
shape->area();
return 0;
}
- 上面代码输出错误!原因是:调用函数area()被编译器设置为基类中的版本,这就是所谓的静态多态或静态链接:函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为area()成员函数在程序编译期间就已经设置好了。对上的代码进行修改,在Shape类中的成员函数area()的声明前放置关键字virtual,如下面所示:
#include "iostream"
using namespace std;
class Shape{
protected:
int width;
int height;
public:
Shape(int a = 0, int b = 0): width(a), height(b){
cout << "基类Shape类的构造函数\n";
}
virtual int area(){ // 此处已经修改,在area()函数的前面加上了virtual关键字!!!
cout << "基类Shape的area()" << endl;
return 0;
}
};
class Rect: public Shape{
public:
Rect(int a = 0, int b = 0): width(a), height(b){
cout << "派生类Rect的构造函数\n";
}
int area(){
cout << "派生类Rect的area()" << endl;
return width * height;
}
protected:
int width;
int height;
};
class Triangle: public Shape{
public:
Triangle(int a=0, int b=0): Shape(a, b){
cout << "派生类Triangle的构造函数\n";
}
int area(){
cout << "派生类Triangle的area()" << endl;
return width * height / 2;
}
};
int main(){
Shape *shape; // 指向类Shape的指针shape
Rect r(10, 7);
Triangle t(10, 5);
shape = &r; // 存储矩形的地址
// 调用矩形的求面积函数
shape->area();
// 存储三角形的地址
shape = &t;
// 调用三角形的求面积函数
shape->area();
return 0;
}
- 此时,编译器看的是指针的内容,而不是它的类型。因此,由于Rect和Triangle类的对象的地址存储在*shape中,所以会调用各自的成员函数area()。每个子类都有都有一个函数area()的独立实现,这就是多太的一般使用方式。有了多态,可以有多个不同的类,这些不同的类都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。
2.虚函数
3.纯虚函数
- 我们希望在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象。但是,在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。见下面的实例,将基类Shape中的虚函数area()改写成下面的样子。虚函数可以不实现(仅仅是定义),不实现(仅仅是定义)的虚函数是纯虚函数。
class Shape{
protected:
int width;
int height;
public:
Shape(int a=0, int b=0):width(a), height(b){
cout << "基类Shape的构造函数\n";
}
// 纯虚函数,在基类中没有给出有意义的实现过程!!!
virtual int area() = 0; // =0表示告诉编译器,函数没有主体,此函数的纯虚函数
}
- 纯虚函数声明如下:virtual void funtion1()=0;,纯虚函数一定没有定义,纯虚函数用来规范派生类的行为,即接口。包含纯虚函数的类是抽象类,抽象类不能定义实例对象,但可以声明指向实现该抽象类的具体类的指针或引用。
- 实现了纯虚函数的派生类,该纯虚函数在派生类中就变成了虚函数,派生类的派生类即孙子类可以覆盖该虚函数,由多态方式调用的时候动态绑定。
- 在一个类中如果存在未定义的虚函数,那么不能直接使用该类的实例,可以理解因为未定义 virtual 函数,其类是抽象的,无法实例化。将报错误:
undefined reference to `vtable for xxx'
- 这和其它语言的抽象类,抽象方法是类似的——我们必须实现抽象类,否则无法实例化,virtual和abstract还是有些区别的。也就是说,如果存在以下代码:
// 在一个类中如果存在未定义的虚函数,那么不能直接使用该类的实例,可以理解因为未定义virtual函数,其类是抽象的,无法实例化。
class Base {
public:
// virtual void tail(); 虚函数没有定义,仅仅是声明而已
};
class People : public Base {
public:
void tail() {
cout << "people" << endl;
}
};
// Base base; 因为Base类中有未定义的虚函数,所以不能直接使用该类的实例化对象,Base类是抽象的,无法实例化
People people;
people.tail();
(&people)->tail(); // &people相当于是指向People类的指针
- 基类中的虚函数或纯虚函数在派生类中依然是虚函数。有时,我们并不希望基类的某个函数在派生类中被重写,在C++11及以后可以用关键字 final来避免该函数再次被重写。
class Parent{
public:
virtual void fun(){
cout << "这个是基类\n";
}
};
class Son: public Parent{
public:
void fun() final{ // 正确,fun在Parent中是虚函数
cout << "这是派生类\n";
}
};
class Children: public Son{
/* 不正确,fun在Son中已经不再是虚函数,不能再被重写
public:
void fun(){
cout << "这是派生类的派生类即孙子类Children\n";
}
*/
};
Son s;
Children c;
Parent* parent;
parent = &s;
parent->fun();
parent = &c;
Son *son;
son = &c;
parent->fun();
son->fun();
- 如果不希望一个类被继承,也可以使用final关键字,则该类将不能被继承。格式如下:
class 类名 final{
......
};
4.参考博客