重载与多态
重载与多态与重写的区别:
重载 :
函数名相同,但是函数的参数不同,调用时根据参数的不同决定调用哪一个函数。
多态 :
函数名相同,函数形参相同。调用时根据函数类型时虚函数还是普通函数决定调用哪一个。
重写 :
若子类和父类的某个函数具有相同的函数名,相同的形参列表,且父类中的函数被定义为虚函数,则子类对该函数的实现被称为函数的重写。
caution!
若函数不声明为虚函数,只能通过类名限定名的方式访问父类或者子类的方法。这叫做隐藏。
若函数声明为虚函数,则可以通过指针访问每一个方法,这叫做覆盖。
接下来,让我们分别对以上的概念进行实现:
一.重载
1.函数重载
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。
如下例子:
#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() { printData pd; // 输出整数 pd.print(5); // 输出浮点数 pd.print(500.263); // 输出字符串 char c[] = "Hello C++"; pd.print(c); return 0; }
运行结果为:
整数为: 5
浮点数为: 500.263
字符串为: Hello C++
2.运算符重载
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
#include<iostream> using namespace std; class Complex { public: Complex(int real1=0,int xu1=0):real(real1),xu(xu1){}; void show()const; friend Complex &operator++(Complex &a);// 返回一个引用即代表当前对象本身,使用引用类型,可以减少内存使用,比如使用返回引用类型,那么它依然还是当前对象,并没有重新构造一个对象。 friend Complex operator++(Complex &b,int); private: int real,xu; }; Complex &operator++(Complex &a) { a.real++; a.xu++; return a;// 返回当前对象(立即可体现出来前置++的值) } Complex operator++(Complex &b,int) { Complex old=b; ++b; return old; } void Complex::show()const { cout<<"real=="<<real<<endl; cout<<"xu="<<xu<<endl; } void main() { Complex c1,c2,c3; ++c1; c3=c2++; c1.show(); c3.show(); c2.show(); }
如果定义为类中的函数
#include<iostream> using namespace std; class Complex { public: Complex(int real1=0,int xu1=0):real(real1),xu(xu1){}; void show()const; Complex &operator++();//后置++,无参数 Complex operator++(int);//前置++,只有一个参数 private: int real,xu; }; Complex & Complex::operator++() { this->real++; this->xu++; return *this; } Complex Complex::operator++(int) { Complex old=*this; ++(*this); return old; } void Complex::show()const { cout<<"real=="<<real<<endl; cout<<"xu="<<xu<<endl; } void main() { Complex c1,c2,c3; ++c1; c3=c2++; c1.show(); c3.show(); c2.show(); }
运行结果均为:
real==1
xu=1
real==0
xu=0
real==1
xu=1
caution!
《C专家编程》中有如下描述(P276,人民邮电出版社):
++a表示取a的地址,增加它的内容,然后把值放在寄存器中;
a++表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值;
二.多态
1.静态多态
#include <iostream> using namespace std; class Shape { protected: int width, height; public: Shape( int a=0, int b=0) { width = a; height = b; } 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; }
运行结果为:
Parent class area
Parent class area
我们没有看到我们想要的结果。
导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。
现在我们在Shape函数area前加virtual。此时,编译器看的是指针的内容,而不是它的类型。因此,由于 tri 和 rec 类的对象的地址存储在 *shape 中,所以会调用各自的 area() 函数。
正如所看到的,每个子类都有一个函数 area() 的独立实现。这就是多态的一般使用方式。有了多态,可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。这类函数就叫虚函数。
虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。