多态性

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

在面向对象方法中,所谓多态性就是不同对象收到相同信息时,产生不同的行为。在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++面向对象程序设计学习笔记(6)的更多相关文章

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

    基本概念 对象: 面向对象程序设计中,对象是描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体,每个对象都是由数据和操作代码两部分构成的. 类: 面向对象程序设计中,类是具有相同的数据 ...

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

    模板与异常处理 模板的概念 模板是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型作为参数. 模板分为函数模板和类模板,它们分别允许用户构造模板类和模板函数 函数模板与模板函数 函数模板实际 ...

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

    派生类与继承 概念 继承允许编程者在已有类的基础上创建新的类,可以从一个或者多个已有类中继承函数和数据,并重新定义或者添加新的函数和数据,已有类称为基类或父类,新类称为派生类和子类. 声明 声明一个派 ...

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

    类与对象(2) string类 C++不仅向下兼容C的字符表示方法,也声明了一种更方便的字符串类型,即string类. 想要使用string类,必须包括头文件string,即要声明 #include& ...

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

    类与对象(1) 结构体与类 结构体的扩充 C++对结构体进行了扩充,它不仅可以含有不同类型的数据,还可以含有函数,结构体的函数可以像访问结构体中的数据一样进行访问. 类的声明 声明类的方法与声明结构体 ...

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

    C++在非面向对象方面的扩充 C++向下兼容C语言,因此C语言中的语法在C++中依然成立 输入输出 C++添加了标准输入流对象cin和标准输出流对象cout来进行输入输出, 例: #include&l ...

  7. 20145213《Java程序设计学习笔记》第六周学习总结

    20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...

  8. Java程序设计学习笔记(一)

    时间:2015-6-2 23:04 程序员,程序猿,程序媛,码农 -------------------------------------------------------   --Java的应用 ...

  9. JavaScript高级程序设计---学习笔记(二)

    面向对象程序设计1.属性类型.定义多属性.读取属性特性对象的属性在创建时都带有一些特征值,JavaScript通过这些特征值来定义它们的行为.这些特性是为了实现JavaScript引擎用的,因此不能直 ...

随机推荐

  1. LeetCode 1248. 统计「优美子数组」

    地址 https://www.acwing.com/solution/leetcode/content/5801/ 题目描述给你一个整数数组 nums 和一个整数 k. 如果某个子数组中恰好有 k 个 ...

  2. WPF 字体设置

    原文:WPF 字体设置 WPF 主界面 更换字体 可全局 但是有的时候有的窗体 字体还是没变 可以做全局样式 <Window x:Class="CLeopardTestWpf.Main ...

  3. 获取系统相关信息 (CPU使用率 内存使用率 系统磁盘大小)

    引言 在软件开个过程中,对于软件的稳定性和使用率也是我们需要关注的 .  使用sigar来监控,简单方便!  使用说明:下载sigar jar及配合sigar的dll文件来用,需要将dll文件放到JD ...

  4. 如何将Javaweb工程的访问协议由http改为https及通过域名访问?

    将javaweb工程的http访问协议更改为https,需要做一下几部操作: 通过jre生成证书 调整tomcat的配置 调整工程的web.xm配置 具体详细过程如下: 一.生成证书 打开cmd切换到 ...

  5. JavaScript判断对象和数组

    在调用后端接口时,由于后端接口的不规范统一,接口最外层在没有数据时返回的是空数组(其实更想要的是空json对象,接口返回的data数据应该统一返回json对象,便于扩展),而在有数据时返回的是json ...

  6. Java8接口新特性

    概述 Java 8中,你可以为接口添加静态方法和默认方法.从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念.猜想设计初衷可能使为了兼容8以下的jdk Java8出来了个函数 ...

  7. oracle里面查询重复数据的方法

    一张person表,有id和name的两个字段,id是唯一的不允许重复,id相同则认为是重复的记录. select id from group by id having count(*) > 1 ...

  8. [算法]LeetCode 152:乘积最大子序列

    题目描述: 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 示例 1: 输入: [2,3,-2,4]输出: 6解释: 子数组 [2,3] 有最大乘积 6.示 ...

  9. vue拖拽组件开发

    vue拖拽组件开发 创建临时vue项目 先查看node和npm版本,怎么安装就不多多bb了 再安装vue-cli npm install vue-cli -g //全局安装 vue-cli 检测是否安 ...

  10. springboot服务的一些问题

    一: springboot踩坑记--springboot正常启动但访问404; 1. spring boot的启动类不能直接放在main(src.java.main)这个包下面,把它放在有包的里面就可 ...