本篇是自己阅读加上自己对“多态”的理解,若有不足之处,感谢指出~
多态
多态最常见的用法就是声明基类类型的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是固定的,因此将始终调用到同一个函数,这就无法实现“一个接口,多种方法”的目的了。
在C++ Primer有关于OOP编程的描述是这样的。
OOP不仅仅是将数据和方法合并为类定义;还有助于创建可重用的代码,这将减少大量的工作(继承、类的复用)。
- 封装(信息隐藏)可以保护数据,使其免遭不适当的访问(使得代码模块化);
- 多态让您能够为运算符和函数创建多个定义,通过编程上下文来确定使用哪个定义;
- 编译时多态:通过重载函数实现
- 运行时多态性:通过虚函数实现
- 继承让您能够使用旧类派生出新类(扩展已存在的代码)。
这些都是面向对象(OOP)所指的特性。还包括:
- 抽象
- 代码的可重用性
在C++面试中很有可能会问你C++的三个特性:封装、继承、多态。
有关于这三点特性,从设计模式中会受益许多,一个好的建议就是去学习设计模式。推荐《Easy搞定设计模式》这本PDF,网上可以搜到。
关于多态,C++ primer给的定义包括多个方面:
- 函数重载
函数多态是C++在C语言的基础上新增的功能,默认参数让您能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)让您能够使用多个同名的函数。术语“多态”指的是拥有多种形式,因此函数多态允许函数可以有多种形式。类似地,术语“函数重载”指的是可以有多个同名的含糊,因此对名称进行了重载。
- 类继承:有些继承关系是多态的,这意味着相同的方法名称可能导致依赖于对象类型的行为。(如下代码所示)
- 运算符重载
class Fruit {
public:
virtual void getName() {
cout << "一种水果" << endl;;
}
void getColor() {
cout << "水果的颜色" << endl;
}
};
/*
还可以声明其他的类和添加您所需要的方法以满足业务需求
您可以尝试创建一个Pear类来更加了解多态行为,更多的可以去了解纯虚函数、抽象等
*/
class Apple : public Fruit {
public:
virtual void getName() {
cout << "我是苹果" << endl;
}
virtual void getColor() {
cout << "红色苹果" << endl;;
}
};
int main()
{
//声明父类和子类的对象
Fruit fruit;
Apple apple;
//声明一个指向父类的父类指针
Fruit *anotherFruit = &fruit;
anotherFruit->getName();
anotherFruit->getColor();
//令父类指针指向子类对象
anotherFruit = &apple;
anotherFruit->getName(); //由于fetName()是虚函数,因此调用时指向虚函数表中该函数所在的位置
/*
有virtual才能发生多态现象,没有virtual就按类型调用。
而getColor()不是虚函数,因此调用基类的getColor()
*/
anotherFruit->getColor();
system("pause");
return 0;
}
输出如下:
再谈“接口”
不同对象调用使用相同方法实现不同操作,也即,方法完成的行为取决于调用该方法的对象。说着有点拗口,我自己理解就是类层面的“重载”。怎么去理解?代码!还是刚才的两个类,额外添加一个Pear类。
//先修改基类,使其变为抽象类
// 1. 抽象类不用定义,只需声明即可
// 2. 抽象类方法前必须有virtual声明
// 3. 抽象类不能实例化对象,但是可以创建对象指针
/*
定义一个水果类抽象基类。用于定操作和规定函数,提供方法
*/
class Fruit {
public:
virtual void getName() = 0;
virtual void getColor() = 0;
};
但凡是纯虚函数,子类继承必须全部重定义!
// 定义苹果类和犁类
class Apple : public Fruit {
public:
virtual void getName() {
cout << "我是苹果" << endl;
}
virtual void getColor() {
cout << "红色苹果" << endl;;
}
};
class Pear : public Fruit {
public:
virtual void getName() {
cout << "我是犁" << endl;
}
virtual void getColor() {
cout << "黄犁" << endl;;
}
};
创建指针对象指向不同的子类,实现不同的操作。
int main() {
// 通过创建基类的指针对象可以实现多台,就不用访问具体的类,只需要根据基类知道其中的操作即可
Fruit *apple = new Apple;
Fruit *pear = new Pear;
apple->getName();
apple->getColor();
pear->getName();
pear->getColor();
return 0;
}
输出如下: