零、引言
重载与多态,OOP的又两大特性。在笔者看来,它们在功能意义上存在着一定的共性——通过重载和多态,将方法的调用简化(比如你想实现数字的加法1+1,又想实现汽车的加法car+car,严格来说二者并不相通,但人类的认识将其中加法的共性提取出来,而计算机则通过重载实现),实现方法的通用性。 这二者也存在着一定的区别,我们先分别讨论它们各自的内容,最后再进行区别的总结。
一、重载
在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。当调用重载函数或者运算符的时候,面对多个定义,编译器就需要进行判断,选择合适的定义进行操作,这一过程则称为重载决策,判断的依据则是根据使用的参数类型。
函数重载:
在同一个作用范围,定义多个功能类似的同名函数,但函数的形式参数(包括参数的个数、类型)必须不同,从而来实现函数的重载。这里,就体现了重载决策是根据参数类型进行判断的。
一个printf()函数重载的例子如下:
#include <iostream>
using namespace std;
class printData
{
public:
//同名函数,形参差异,函数重载
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};
int main(void)
{
printData pd;
// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);
return 0;
}
运算符重载:
运算符就是我们常用的+.-,*,/(加减乘除)等符号,它们可以看成特殊的函数。实现运算符的重载的方式有二:
定义为类成员函数,则只需要一个参数:
return_tyep operator重载符号(type_name parameter)
//例子如下
Box operator+(const Box&);
定义为普通的函数(非成员函数),则需要两个参数:
return_tyep operator重载符号(type_name parameter1,type_name parameter2)
Box operator+(const Box&, const Box&);
+运算符的重载示例如下,其实现了Box类型的加法:
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
二、多态
“当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态”。通俗来说,就是对于基类和派生类,在调用成员函数时,会根据对象的特点(不同的派生类),产生不同的响应。(还是拿动物的例子来说,就好像胎生动物和卵生动物,生殖()是动物的一个方法,但是对于胎生动物和卵生动物,它们的实现方式是不同的)
多态是通过虚函数实现的,虚函数是在基类中使用关键字 virtual 声明的函数。对于每个子类,都有一个同名的函数来独立实现,这种称为动态多态,或动态链接。
基类中,虚函数可以没有内容,则称为纯虚函数,其为虚函数的一个特例,=0用来告诉编译器该虚函数为纯虚函数。
如果未指定virtual虚函数,子类的被调用函数为基类的版本,则称为静态多态,或静态链接。
一个虚函数、纯虚函数以及静态多态的例子如下:
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
/*----------------虚函数----------------------*/
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
/*-------------------------------------------*/
/*----------------纯虚函数----------------------*/
/*
virtual int area()=0;
*/
/*----------------------------------------------*/
/*----------------静态多态----------------------*/
/*
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
*/
/*----------------------------------------------*/
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// 程序的主函数
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存储矩形的地址
shape = &rec;
// 调用矩形的求面积函数 area
shape->area();
// 存储三角形的地址
shape = &tri;
// 调用三角形的求面积函数 area
shape->area();
return 0;
}
三、重载与多态的关系
前面提到,重载和多态存在一定的共性,它们间的区别主要有:
- 重载:函数名相同,形参不同,根据参数和形参选择合适的实现函数(通常是同一类内 )
- 多态:函数名相同,形参相同,根据调用的对象所属的类选择对应的函数(不同的派生类间)
四、结语
C++的面向对象基本特性就介绍到此,其余内容笔者计划在实践中去了解和认识。等忙完有关任务后,后期还会进一步精炼对C++的理解。
以上内容来自笔者根据菜鸟教程的总结和归纳,并添加一些内容和注释