C/C++基础知识总结——多态性

1. 多态性的概述

1.1 多态是指同样的消息被不同类型的对象接收时导致不同的行为。所谓消息是指对垒的成员函数的调用,不同行为是指不同的实现。

1.2 多态的实现

  (1) 实现角度讲多态可分为两类:编译时的多态和运行时的多态。

    ① 编译时:编译过程中确定了同名操作的具体操作对象。静态绑定。

    ② 运行时:程序运行时动态确定操作针对的具体对象。动态绑定。

2. 运算符重载

2.1 运算符重载就是对已有的运算符赋予多重含义,使同一个运算符作用域不同类型的数据导致不同的行为。

2.2 运算符重载的实质是函数重载。在实现过程中,首先把制定的运算表达式转化为对运算符函数的调用,然后把运算对象转化为运算符函数的实参,然后根据实参类型确定调用的函数,这个过程是在编译过程完成的。

2.3 运算符重载的规则

  (1) 只能重载C++中已经有的运算符

  (2) 重载之后的优先级和结合型不变

  (3) 运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。因此重载后的功能因该与原功能类似,不能改变操作对象个数,同时至少有一个操作对象是自定义类型

2.4 运算符重载的形式:重载为类的非静态成员函数和重载为类的成员函数

  (1) 重载为类的成员函数

    返回类型 operator 运算符(形参表)

    {}

  (2) 重载为非成员函数。此时有可能需要将此函数声明为友元函数。

    返回类型 operator 运算符(形参表){

    }

2.5 运算符重载为成员函数

  (1) 双目运算符

    左操作数是对象本身数据,由this指针指出,有操作数是通过重载函数的参数表实现

    双目运算符B。 为了实现 oprd1 B oprd2。 应该把B实现为oprd1的重载运算符函数,该函数有一个形参,类型是oprd2对应的类型。重载后,oprd1 B   oprd2即为 oprd1.operator B(oprd2)

    例子:  

Complex operator+ (const Complex &c2) const;
Complex Complex::operator+ (const Complex &c2) const{
    reuturn ...;
}

  (2) 前置单目运算符

    操作数由对象的this指针指出,无需参数

    比如’-‘等运算符U, U oprd.应该重载为oprd类型的成员函数,没有形参。表达式 U oprd即为 oprd.U()

    例子:

Clock& operator++();
Clock& Clock::opertor++(){
    reutrn ..;
}

  (3) 后置单目运算符

    操作数由对象的this指针指出。

    比如++和--,此时,运算符应重载为对象的成员函数,这个函数有一个int 的形参,这个参数没有用处,只是为了区分前置和后置

    例子:

Clock operator++(int);
Clock Clock::operator++(int)
{
      return ...;
}

2.6 运算符重载为非成员函数

  (1) 对于双目运算符B,如果要实现oprd1 B oprd2,其中oprd1和oprd2中只要有一个具有自定义类型,就可以将B重载为非成员函数,函数的形参oprd1 和 oprd2。经过重载后oprd1 B oprd2 就相当于函数调用operator B(oprd1, oprd2)

  (2) 其他的类似,就是多个参数

  (3) 例子:

class Complex{
public:
   friend Complex operator+( const Complex &c1, const Complex &c2);
   friend Complex ostream & operator<<( ostream &out, const Complex &c);
private:
   ...
}

Complex operator+(const Complex &c1, const Complex &c2){
   return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

ostream & operator<<(ostream &out, const Complex &c)
{
   out<<c.imag;
   return out;
}

3. 虚函数

3.1 虚函数是动态绑定的基础。虚函数必须是非静态的成员函数。虚函数经过派生后就可以实现运行过程中的多态。

3.2 如果需要通过基类的指针指向派生类的对象,并且访问某个与基类同名的成员,那么首先在基类中将这个同名函数声明为虚函数。

3.3 一般虚函数成员

  (1) 声明语法:

    virtual 函数类型 函数名(形参表)

  (2) 虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现的时候。

  (3) 如果基类声明了虚函数,而派生类相应函数没有声明virtual,那么系统根据如下规则:

    ① 该函数是否与基类的虚函数有相同名称

    ② 该函数是否与基类的虚函数有相同的参数

    ③ 该函数是否与基类的虚函数有相同的返回值或者满足复制兼容规则的指针、引用型的返回值

  (4) 只有虚函数是动态绑定的,如果派生类需要修改基类的行为(即重写与基类函数同名的函数),就应该在基类中将相应的函数声明为虚函数。而基类中声明  的非虚函数,通常代表那些不希望被派生类改变的功能,也是不能实现多态的。一般不要重写继承而来的非虚函数,那会导致通过基类指针和派生类指针或者对  象调用同名函数时产生不同的后果,从而引起混乱。

3.4 虚析构函数

  (1) 如果基类声明为虚析构函数,则派生类的构造函数也为虚函数。

  (2) 如果需要用基类的指针调用派生类的析构函数则一定要声明虚析构函数。

4. 纯虚函数与抽象类

  建立抽象类就是为了通过多态使用成员函数。抽象类自身无法实例化,只能通过继承机制,生成非抽象派生类,再进行实例化。

4.1 纯虚函数

  (1) 纯虚函数是一个在基类中声明的虚函数,在基类中没有定义具体的操作,要求各派生类根据实际需要给出自己的定义。

  (2) 声明格式:

    virtual 函数类型函数名(参数表)= 0;//后面的 = 0

  (3) 声明为虚函数后,基类中就可以不再给出函数的实现部分。

  (4) 如果将析构函数声明为纯虚函数,则必须在基类中给出它的实现,因为派生类的析构函数执行完后需要调用基类的析构函数。实际上,基类中是可以给出纯  虚函数的函数体,但是即使给出,也必须由派生类覆盖。

4.2 抽象类

  (1) 带有纯虚函数的类是抽象类。抽象类的主要作用是通过它为一个类族建立一个公共接口。

  (2) 抽象类派生出新的类后,如果派生类给出所有纯虚函数的函数实现,这个派生类就可以定义自己的对象。

  (3) 抽象类不能实例化。

 

作者:viczzx 出处:http://www.cnblogs.com/zixuan-zhang 欢迎转载,也请保留这段声明。谢谢!

原文地址:https://www.cnblogs.com/zixuan-zhang/p/3335099.html