[总结]C++真是博大精深(四)

C++的多态性

(最终解释权归原文作者所有,侵权必究)

1、多态性是面向对象程序的重要特性之一。多态性就是指一个接口,多种方法。从实现的角度出发可以将多态分为编译时多态和运行时多态。编译时多态是指在多个函数的函数名(包括运算符)相同的情况下,编译器在编译阶段就能够根据函数参数的个数或类型不同确定要调用的函数。这种静态多态是通过函数重载或运算符重载实现的。运行时多态是指在程序运行时才确定要调用的函数。这种动态多态是通过虚函数机制实现的。

2、运算符的重载是通过创建运算符重载函数来实现的。运算符重载函数有三种定义方式:作为类的成员函数、作为类的友元函数、作为类外定义的普通函数。

  ⑴由于非静态成员函数有一个隐藏的this指针参数,因此使用成员函数重载双目运算符时,只需给出一个参数,该参数作为运算符的第二个操作数。运算符重载可显式调用,也可隐式调用。

①在类的内部,定义成员运算符重载函数的语法形式为:函数返回值类型 operator 运算符(形参表){函数体}。

②如果实在类的外部定义成员运算符重载函数,则需要首先在类的内部进行声明,然后在类外进行定义,其形式为:class 类名{…函数返回值类型operator 运算符(形参表);};函数返回值类型 类名::operator 运算符(形参表){ 函数体 }

  ⑵通过成员运算符重载函数能够实现两个类对象的运算。但是,对于左操作数是基本数据类型,而右操作数是类对象的情况,通过成员运算符函数就不能实现重载运算了。在这种情况下,需要通过(非成员函数类型的)友元函数实现运算符的重载。

    ①有的C++编译系统,没有完全实现C++标准,对友元运算符重载函数支持锝不好,当使用命名空间和不带后缀的.h的iostream文件时,可能会出现与友元运算符重载函数有关的编译错误。要用#include<iostream.h>  #include<string>

    ②通过友元函数进行运算符重载,可以使基本数据类型出现在运算符左边,类对象出现在运算符右边。

    ③由于成员运算符重载函数的this指针参数是隐含的,因此从定义形式上看:对双目运算符,成员运算符重载函数的参数表中只有一个参数,而友元运算符重载函数的参数表中有两个参数;对于单目运算符,成员运算符重载函数的参数表中通常没有参数,而友元运算符重载函数的参数表中通常有一个参数。

  ⑶如果运算符重载函数是另一个类的友元,那么它可以访问该类的私有成员。当运算符函数是类外的普通函数时,由于该函数不是类的友元,因此只能访问类的公有成员。

3、自增运算符++和自减运算符—属于单目运算符。这两种运算符都有前缀和后缀两种形式,在进行重载时要进行区分。前缀自增成员函数的声明为:CPoint operator++();后缀自增成员函数的声明为:CPointoperator++(int i);该整型参数并没有被实际使用,该参数设立的目的是与前缀自增成员函数有所区别。当C++编译系统遇到后缀自增运算符时,会自动调用该带整型参数的函数,当遇到前缀自增运算符时,自动调用不带显式参数的前缀自增成员函数。

4、⑴C++中绝大多数运算符都可以被重载,只有以下几个运算符不能被重载:.(成员访问运算符,也为点运算符)、.*(成员指针访问运算符)、::(作用域运算符)、?:(条件运算符)、sizeof(数据所占内存大小运算符)。

  ⑵只允许对已有的C++运算符进行重载,而不能创建新的运算符。

  ⑶通常来说,运算符重载后的功能应与原有功能类似,这样符合人们的使用习惯,不易引起混淆。

  ⑷运算符重载不能改变其原有操作数的个数。

  ⑸运算符重载不能改变运算符原有的优先级和结合特性。

  ⑹运算符重载函数的参数中至少有一个应是类(结构体)对象或类(结构体)对象的引用,而不能都是基本数据类型。

5、虚函数允许函数调用与函数体之间的联系在程序运行时才建立,也就是运行时才决定如何动作。通过具有相同函数原型的同名函数定义成虚函数,基类指针就能根据所指对象的不同,调用不同类的虚函数,从而实现运行时的多态性。

  ⑴在派生类中对虚函数进行重新定义时,这些同名函数的函数原型必须完全相同。当基类虚函数的返回类型是基类型的引用(或指针)时,派生类虚函数的返回类型可以是基类虚函数返回类型的派生类的引用(或指针)。

  ⑵一旦基类中的某成员函数被指定为虚函数,那么,派生类(以及派生类的派生类,以此类推)中对其重定义的成员函数均为虚函数。这时,派生类虚函数的声明前可以省略关键字virtual。

  ⑶派生类中对基类虚函数进行重新定义,称为函数覆盖。

  ⑷在定义虚函数时,只需在函数声明时加上关键字virtual,而在类外进行函数定义时,不能再加关键字virtual。

  ⑸静态成员函数不能是虚函数。构造函数不能是虚函数。

6、通过将析构函数定义为虚函数,可以保证在使用delete运算符删除指向派生类对象的基类指针时,能够调用派生类的析构函数。通常,在继承层次的根类中应定义一个虚析构函数。

7、有时候,基类用于表示一个抽象的概念,并不直接与具体的对象想联系,而是通过派生类与具体的对象相联系。对于这种表示抽象概念的基类,可以将其定义为抽象类。

⑴抽象类的特点是不能创建该类的对象。在C++中,含有(或继承)一个或多个纯虚函数的类称为抽象类。抽象类可以作为其他类的基类。

⑵纯虚函数是一种特殊的虚函数,这些虚函数只有声明,而没有定义。纯虚函数在类中进行声明的形式为:virtual 函数返回值类型 函数名(参数表)=0;

⑶抽象类不能作为函数参数或函数返回值类型,也不能作为显式转换的类型,但可以定义抽象类的指针和引用。

⑷如果在抽象类的派生类中没有对纯虚函数对应的同名函数进行定义,那么该函数在派生类中仍然是纯虚函数,该派生类仍然是一个抽象类。

⑸纯虚函数声明最后的=0表示该函数是纯虚函数。纯虚函数没有函数体,不能被调用。

⑹抽象基类可以作为一个类族提供一个框架,用于声明公共的接口,而接口的具体实现由派生类来完成。

模板

1、模板的目的是实现数据类型的参数化,即将数据类型作为参数。这样,同样一段代码,可以处理不同数据类型的参数。模板提供了将代码与数据类型相脱离的机制。模板可分为函数模板和类模板两种类型,在定义函数或类时,可以将数据类型作为模板的参数。模板适用于函数或类的功能相同,而函数或类所涉及的数据类型不同的场合。

2、函数模板是指建立一个通用函数,该函数的某些形参类型或函数返回类型不具体指定,使用类型参数标识符代替。在调用函数模板时,会根据实参的类型来取代函数模板中的类型参数标识符,从而生成实际的函数。

  ⑴template <typenameT>,也可以写成template <class T>,模块的定义以关键字template开头,而关键字typename(或class)表示后面的标识符是模块参数(数据类型参数)。

  ⑵在调用函数模块时生成的函数模块实例称为模板函数,此实例化的过程称为函数模板的实例化。

  ⑶函数模板在实例化时,编译器根据实际参数的数据类型进行函数模板类型参数的替换,生成一个具体函数,然后再对该函数进行编译。

  ⑷在template语句和函数模板定义语句之间不能有别的语句。

  ⑸定义函数模板时可以使用多个类型参数。

3、类模板是这样一种通用类:在定义类时不指明某些数据成员、成员函数的参数或返回值的数据类型,而是用类型参数取代。类模板也成为参数化的类,它可以生成多个成员和功能相似的类,这些类的区别仅仅在于某些数据成员、成员函数的参数或返回值的数据类型不同。

  ⑴类模板被实例化后,就形成了模板类。然后就可以定义这些模板类的对象、指针或引用。

  ⑵使用类模板定义对象、指针变量或引用时,其基本形式为:类模板名 <实际类型名> 对象名或指针变量名或引用变量名。

  ⑶类模板的成员函数可以在类模板体内进行定义,如果在类模板的体外定义含有类型参数的成员函数,要在成员函数的定义前面加上模板声明,在成员函数名前面添加类模板名<类型参数>。

    template<typename 类型参数>(或template <class 类型参数>)

    函数返回值类型 类模板名<类型参数>::成员函数名(形参表){函数体}

  ⑷同函数模板相类似,类模板也可以有多个类型参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值