多态从实现的角度可以划分为:编译时多态和运行时的多态。
运算符重载
运算符重载即静态多态,是对已有的运算符赋予多重含义,运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作。运算符函数的定义与其他函数的定义类似,唯一的区别是运算符函数的函数名是由关键字 operator 和其后要重载的运算符符号构成的。把指定的运算表达式转化为对运算符函数的调用,运用对象转化为运算符函数的实参。
• 运算符重载格式
函数类型 operator运算符 (形参){ …… }
• 运算符重载规则
当重载为类的成员函数的情况,形式参数个数=原操作数个数-1(后置++、--除外)。
当重载为类友元函数的情况,形式参数个数=原操作数个数。
除了类属关系运算符 "." 、成员指针运算符 ".*" 、作用域运算符 "::" 、sizeof 运算符和三目运算符 "?:" 以外,C++ 中的所有运算符都可以重载。 重载运算符限制在 C++ 语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
//复数类成员函数自增 #include<iostream> using namespace std; class complex{ private: int real,imag; public: complex(int r,int i){ real=r; imag=i; } complex &operator++(); complex operator++(int); void display(){ cout << "("<< real << ","<< imag << ")" <<endl; } ~complex(){ } }; complex &complex::operator++(){ real++; imag++; return *this; } complex complex::operator++(int){ complex old=*this; real++; imag++; return old; } int main(){ complex c1(1,1); (++c1).display(); (c1++).display(); return 0; }
需要注意的是,前自增函数加&是方便连续自增,用于返值返回的是引用而不是赋值,可以减少内存,类似于函数传值和传地址。
也就是说,该函数返值会通过地址传送的方式给到函数调用者要求的返回值,这样可以节省对象赋值造成的内存浪费,通常用于返值是大型对象(而不是简单变量类型)的时候。
//复数类有元函数自增相加 #include<iostream> using namespace std; class complex{ private: int real,imag; public: complex(int r,int i){ real=r; imag=i; } friend complex operator+(complex &c1,complex &c2); friend complex operator++(complex &c2,int); void display(){ cout << "(" << real << ","<< imag << ")" <<endl; } ~complex(){ } }; complex operator+(complex &c1,complex &c2){ return complex(c1.real+c2.real,c1.imag+c2.imag); } complex operator++(complex &c2,int){ //后自增 complex c=c2; c2.real++; c2.imag++; return c; } int main(){ complex c1(1,1),c2(2,2); complex c3=c1+c2; c3.display(); (c1++).display(); return 0; }
虚函数
虚函数声明只能在出现类定义中的函数原型生声明时,不能在成员函数实现的时候。需要注意的是动态多态需要满足三个条件:(1).类间满足兼容性规则(即派生类代替基类);(2).要声明虚函数;(3).需要用指针或引用来调用虚函数。
#include<iostream> using namespace std; class A{ public: /*virtual*/ void funPrint(){ cout<<"funPrint of class A"<<endl; } }; class B:public A{ public: /*virtual*/void funPrint(){ cout<<"funPrint of class B"<<endl; } }; int main(){ A *p; //定义基类的指针 A a; B b; p=&a; p->funPrint(); p=&b;//希望派生类的对象代替基类的对象 (把派生类地址给基类指针) p->funPrint(); }
我么希望此程序中能够用派生类代替基类,即的第二个输出应为B而不是A,但实际上不管引用的实例是哪个类的当你调用的时候系统会调用左值那个对象所属类的方法。比如说 上面的代码类A B都有一个funPrint 函数,因为p是一个A类的指针,所以不管你将p指针指向类A或是类B,最终调用的函数都是类A的funPrint 函数。这就是静态联篇,编译器在编译的时候就已经确定好了。可是如果我想实现跟据实例的不同来动态决定调用哪个函数呢?这就须要用到 虚函数(也就是动态联篇),我们只用把成员函数变成虚函数加上virtual关键字 ,再运行就可以看见达到了我们预期的效果。
注意:构造函数不能是虚函数,但析构函数可以
纯虚函数
带有纯虚函数的类就是抽象类,它是声明一个函数但不实现它,给派生类提供一个接口,让派生类去实现它。
#include<iostream> using namespace std; class Vehicle{ public: virtual void PrintTyre()=0; //纯虚函数定义 }; class Camion:public Vehicle{ public: virtual void PrintTyre(){ cout<<"Camion tyre four" << endl;} }; class Bike:public Vehicle {public: virtual void PrintTyre(){ cout<<"Bike tyre four"<<endl; } }; int main() { Camion c; Bike b; b.PrintTyre(); c.PrintTyre(); }
虚基类
当某类的部分或全部直接基类是从另一个共同派生而来时,这些从同一基类继承来的函数具有相同的函数名,在内存中会存在多个相同副本。可以用唯一标识符来区分它们,但更好的方法是将他们设置为虚基类。设置为虚基类后从不同类继承来的同名函数成员在内存中只存在一个副本。
• 虚基类语法表示
class 派生类名: virtual 继承方式 基类名{ …… }
看下面代码:
#include<iostream> using namespace std; class A{ public: int iValue; }; class B:virtual public A{ public: void bPrintf(){cout<<"This is class B"<<endl;} }; class C:virtual public A{ public: void cPrintf(){cout<<"This is class C"<<endl;} }; class D:public B,public C{ public: void dPrintf(){cout<<"This is class D"<<endl;} }; int main(){ D d; cout<<d.iValue; //正确 }
在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。例如iValue这个成员,从类 D这个角度上来看,它是从类B与类C继承过来的,而类B C又是从类A继承过来的,但它们只保留一个副本。因此在主函数中调用d.iValue时就不 会产生错误。