C++面向对象程序设计学习笔记(6)

多态性

编译时的多态性与运行时的多态性

在面向对象方法中,所谓多态性就是不同对象收到相同信息时,产生不同的行为。在c++程序设计中,即“一个接口,多种方法”

在C++中,多态性的实现和联编这一概念相关,一个源程序经过编译、连接,称为可执行文件的过程即为把可执行代码联编在一起的过程。其中在运行前完成的称为静态联编,又称前期联编,而在运行时完成的称为动态联编,也称后期联编

静态联编支持的多态性称为编译时多态性,也称静态多态性,在c++中通过函数重载和模板实现,动态联编所支持的多态性称为运行时多态性,也称动态多态性,c++通过虚函数实现

运算符重载

运算符重载是通过创建运算符重载函数实现的,运算符重载函数可以是类外定义的普通函数,也可以是类的成员函数或友元函数。

在类外定义的运算符重载函数

c++为运算符重载提供了一种方法,即在进行运算符重载时,必须定义一个运算符重载函数,其名字为operator,后随一个要重载的运算符。

//例:重载'+'号
类名 operator+(类名1 对象名1,类名2 对象名2)
{
......
}
//即可使用以下两种方式调用'+'号重载
对象名1+对象名2;
operator+(对象名1,对象名2);

注意:

1.c++中绝大部分运算符允许重载,但以下运算符例外

 .        成员方位运算符

 .*        成员指针访问运算符

 ::        作用域运算符

 Sizeof     长度运算符

 ?:        条件运算符

2.c++只能对已有的C++运算符进行重载

3.一般来讲,重载的功能应当与原有的功能相类似

4.重载不能改变运算符的操作对象的个数

5.重载不改变运算符原有的优先级

6.重载不能改变运算符原有的结合特性

7.运算符重载函数的参数至少有一个是类对象(或类对象的引用)

8.一般而言,用于类对象的运算符必须重载,但是赋值运算符'='例外,不必用户进行重载

友元运算符重载函数

在c++中,可以把运算符重载函数定义为某个类的友元函数,称为友元运算符重载函数

语法形式

1.在类内部,定义友元运算符重载函数的格式如下

friend 函数类型 operator 运算符(形参表)
{
函数体
}

2.在类外部,定义友元运算符重载函数的格式如下

//在类中,声明友元函数重载函数原型
class X
{
......
friend 函数类型 operator 运算符 (形参表);
......
}
//在类外部,定义友元运算符重载函数
函数类型 operator 运算符 (形参表)
{
函数体
}

X为类名;函数类型指定了友元运算符函数的返回值类型;operator是定义运算符重载函数的关键字;运算符即是要重载的运算符名称

双目运算符重载
class 类名
{
......
friend 函数类型 operator @ (形参表);
......
}
函数类型 operator@(类名1 对象名1,类名2 对象名2)
{
......
}
//即可使用以下两种方式调用重载
对象名1@对象名2;
operator@(对象名1,对象名2);
//@即为运算符
单目运算符重载
class 类名
{
......
friend 函数类型 operator @ (形参表);
......
}
函数类型 operator@(类名 对象名)
{
......
}
//即可使用以下两种方式调用重载
@对象名;
operator@(对象名);
//@即为运算符

说明:

1.运算符重载函数operator@可以返回任何类型,甚至是void类型

2.有的运算符不能定义为友元运算符重载函数,如赋值运算符'=',下标运算符'[]',函数调用运算符'()'等

成员运算符重载函数

在c++中,可以把运算符重载函数定义为某个类的成员函数,称为成员运算符重载函数

语法形式

1.在类内部,定义成员运算符重载函数的格式如下

函数类型 operator 运算符(形参表)
{
函数体
}

2.在类外部,定义友元运算符重载函数的格式如下

//在类中,声明成员运算符重载函数原型
class X
{
......
函数类型 operator 运算符 (形参表);
......
}
//在类外部,定义成员运算符重载函数
函数类型 X:: operator 运算符 (形参表)
{
函数体
}

X为类名;函数类型指定了友元运算符函数的返回值类型;operator是定义运算符重载函数的关键字;运算符即是要重载的运算符名称

双目运算符重载
class 类名
{
......
函数类型 operator @ (形参表);
......
}
函数类型 类名::operator@(类名 对象名)
{
......
}
//即可使用以下两种方式调用重载
对象名1@对象名2;
对象名1.operator@(对象名2);
//@即为运算符
单目运算符重载
class 类名
{
......
函数类型 operator @ ();
......
}
函数类型 类名::operator@()
{
......
}
//即可使用以下两种方式调用重载
@对象名;
对象名.operator@();
//@即为运算符
注意

1.当对类对象与其他类对象使用双目运算符进行运算时,若需要进行随意放置两种类对象,则需要使用友元运算符

"++"和"--"的重载

自增运算符"++"和自减运算符"--"放置在变量前面与后面,其作用是有区别的

对于前缀方式++ob,可以重载为 ob.operator++();operator++(X &ob);

对于后缀方式ob++,可以重载为 ob.operator++(int);operator++(X &ob,int);

调用时,参数int一般被传递给值0

"--"运算符类似

注意:

由于友元运算符重载函数没有this指针,所以不能引用this指针所指向的对象。使用友元函数重载时,应采用对象引用参数传递数据

赋值运算符"="的重载

对任意一类X,若不存在自定义赋值运算符重载,则会自动生成默认的赋值运算符函数

但在某些特殊情况下,如类中存在指针时,默认的赋值运算符重载会产生问题,因此必须显示定义一个赋值运算符重载函数

注意:

类的赋值运算符"="只能重载为成员函数,而不能重载为友元函数,因为会导致赋值语句的混乱

下标运算符"[]"的重载

运算符重载函数只能定义为成员函数,形式如下:

返回类型 类名 :: operator [] (形参)
{
//函数体
}

注意:

形参只能由一个参数

类型转换

系统预定义类型间的转换

类型转换是将一种类型的值转换为另一种类型值。C++提供两种类型转换方式:一种是隐式类型转换;另一种是显式类型转换。

隐式类型转换

隐式类型转换主要遵循以下规则:

1.在赋值表达式A=B的情况下,运算符右端的B的值需转换为A类型后进行赋值

2.char或short类型变量与int类型变量进行运算时,将char或short类型变量转换为int型

3.操作对象不一致时,运算前级别低的类型自动转化为级别高的类型

显式类型转换

1.(int)i //c语言形式

2.int(i) //c++形式

C++保留C的用法,但提倡使用C++形式

类类型与系统预定义类型间的转换

通过转换构造函数进行类型转换

转换构造函数的作用是将一个其他类型的数据转换为它所在类的对象

实现方式:

在类中定义一个只有一个参数的构造函数,参数是待转换类型的数据,在函数体中指定转换的方法

例:

Complex(double r)
{
real=r;imag=0;
}

既可以用以下形式进行类型转换

类名(待转换类型的数据)

注意:

1.转换构造函数的实质也是一种构造函数,遵循构造函数的一般规则,但只有一个参数

2.转换构造函数也可以将另一个类的对象转换为构造函数所在类的对象

通过类型转换函数进行类型转换

类型转换函数的作用是将一个类的对象转换为另一类型的数据

一般格式为

operator 目标类型()
{
函数体
}

其中,目标类型为希望转换成的类型名,既可以是预定义的标准数据类型也可以是其他类的类型

返回值的类型是该函数的目标类型

注意:

1.类型转换函数只能定义为成员函数

2.类型转换函数既没有参数,也不能在函数体前指定类型

3.必须有return语句

4.一个类可以定义为多个类型转换函数

虚函数

虚函数是重载的另一种表现形式,是一种动态的重载方式。

虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,即动态联编。

引入

C++规定:基类对象的指针可以指向它的公有派生的对象,但是当其指向公有派生类对象时,它只能访问派生类从基类继承来的成员,而不能访问公有派生类中定义的成员。

当引用虚函数后,能实现动态调用的功能

定义

虚函数就是在基类中被关键字virtual说明,并在派生类中重新定义的函数。

作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数

虚函数的定义是在基类中进行的,在基类中需要定义为虚函数的成员函数的声明中冠以关键字virtual,方法如下:

virtual 函数类型 函数名 (形参表)
{
函数体
}

在基类中某个成员函数被声明为虚函数后,虚函数即可在一个或多个派生类中重新定义,在重新定义时,包括函数原型、函数名等都必须与基类中原型完全相同。

注意:

1.若在基类中,只声明虚函数原型,则类外定义时,不必加virtual

2.在派生类重新定义虚函数时,关键字virtual可写可不写,但最好写上

3.若派生类没有对基类的虚函数重新定义,则公有派生继承其基类的虚函数,无论被继承多少次,依然保持其虚函数特性

4.虚函数必须为所在类的成员函数,而不是友元函数或静态成员函数

5.只有通过基类指针访问虚函数时才能获得运行时的多态性

虚析构函数

C++中不能声明虚构造函数,但是可以声明虚析构函数

一般格式如下virtual ~类名();

虚函数与重载函数之间的关系

在一个派生类中重新定义基类的虚函数时函数重载的另一种形式,但不同于普通的函数重载

当普通函数重载时,其函数参数或参数类型必须有所不同,返回类型也可以不同

但在重载虚函数时,要求必须完全相同

多重继承与虚函数

多重继承可视为多个单继承的组合,因此多重继承与单继承有相似之处

纯虚函数和抽象类

有时基类会表示一种抽象的概念,它并不与具体的事物相联系。

纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但要求它的派生类中根据需要对它进行定义,或仍然说明为纯虚函数

一般形式如下

virtual 函数类型 函数名(参数表) =0;

格式与一般的虚函数相似,但在后面添加了一个"=0"

声明为纯虚函数后,基类中不再给出函数实现部分

纯虚函数的作用在于为其派生类保留一个函数名,以便派生类重新定义

纯虚函数没有函数体,"=0"仅为表示其为纯虚函数,其不能被调用

若果一个类中至少有一个纯虚函数,则称该类为抽象类

抽象类的规定:

1.抽象类只能作为其他类的基类,不能建立抽象类对象

2.不能用作参数类型,函数返回类型或显式转换的类型。但可以声明指向抽象类的指针变量,该指针可指向它的派生类

3.若抽象类的派生类没有再次声明纯虚函数,则该函数在派生类中依然为纯虚函数,该派生类为抽象类

上一篇:C++面向对象程序设计学习笔记(4)


下一篇:python3 开发面试题(面向对象)6.6