1、多态性

1.1 什么是多态?

多态是指相同消息不同对象接收后导致不同的行为,所谓消息是指对类成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数。

消息在C++编程中指的是对类的成员函数的调用。

举例解释

使用运算符“+”可以实现对整数,浮点数,双精度浮点数的加法运算。“+”是消息,被不同类型的对象接收后,采用不同方式进行运算

1.2 多态类型

1.2.1 总览

多态类型 介绍    
重载多态 普通函数重载和类成员函数重载(运算符) 专用多态 静态绑定
编译时多态

强制多态

强制类型转换

强制多态就是将一个变量的类型进行转换,以满足一个函数运算的要求。

静态绑定
编译时多态
包含多态

虚函数

包含多态是指类族中不同类的同名成员函数实现的操作不相同。

通用多态 动态绑定
运行时多态
参数多态

类模板

由类模板实例化得到的所有类都有相同的操作,但是被操作对象的类型不同,这就是参数多态。

静态绑定
编译时多态

1.2.2 编译时多态/运行时多态

从实现角度来看,多态有两种类型,编译时多态和运行时多态

编译时多态 编译过程中确定了同名操作的具体对象
运行时多态 运行过程中确定了同名操作的具体对象

这种确定调用同名函数的哪个函数的过程就叫做联编或者绑定。一般称其为绑定。绑定实际上就是确定某个标识符对应的存储地址的过程。按照绑定发生的阶段的不同可以分为:静态绑定动态绑定

如果绑定过程发生在编译链接阶段,则称为静态绑定。在编译链接过程中,编译器根据类型匹配等特征确定某个同名标识究竟调用哪一段程序代码,也就是确定通过某个同名函数到底调用哪个函数体。

如果绑定过程发生在程序运行阶段,则称为动态绑定。在编译链接过程中无法确定调用的具体函数,就要等到程序运行时动态确定。包含多态就需要使用动态绑定实现。

静态绑定
编译时多态

绑定过程发生在编译链接阶段

重载多态、强制多态、参数多态

动态绑定
运行时多态

绑定过程发生在程序运行阶段

包含多态

2、重载多态-运算符重载

2.1 引子——复数类

定义一个复数类:

class Complex //复数类
{
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
void display() const; //显示复数的值
private:
double real;
double imag;
};

定义复数类的对象:

Complex a(10, 20), b(5, 8);

我们希望对a和b进行加法运算,

  1. 可以写个函数,类内的函数或类外的函数都行。不建议
  2. 对“+”运算符进行重载。(“+”在作用于Complex类型对象时会执行对应程序)

2.2 例8-1 复数类加减法运算重载——成员函数形式

先看看程序应该怎么写,再介绍运算符重载

/*
8-1 P309
复数类加减法运算重载——成员函数形式
*/
#include <iostream>
using namespace std;
class Complex //复数类
{
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
Complex operator+(const Complex &c2) const; //运算符+重载的成员函数
Complex operator-(const Complex &c2) const; //运算符-重载的成员函数
void display() const; //显示复数的值
private:
double real;
double imag;
};
Complex Complex::operator+(const Complex &c2) const
{
return Complex(real + c2.real, imag + c2.imag); //创建一个临时对象作为返回值,这里调用了Complex类的构造函数
}
Complex Complex::operator-(const Complex &c2) const
{
return Complex(real - c2.real, imag - c2.imag); //创建一个临时对象作为返回值,这里调用了Complex类的构造函数
}
void Complex::display() const
{ //显示复数中的内容
cout << "(" << real << "," << imag << ")" << endl;
}
int main()
{
Complex c1(5, 4), c2(2, 10), c3; //定义复数类的对象
cout << "c1=";
c1.display();
cout << "c2=";
c2.display();
c3 = c1 - c2; //使用重载运算符完成复数减法
cout << "c3=c1-c2=";
c3.display();
c3 = c1 + c2; //使用重载运算符完成复数加法
cout << "c3=c1+c2=";
c3.display();
return 0;
}

运行结果:

c1=(5,4)
c2=(2,10)
c3=c1-c2=(3,-6)
c3=c1+c2=(7,14)

2.3 运算符重载规则

重载:函数的另一种调用形式,只是写着方便,看着方便,本质还是调用函数

  1. C++中几乎所有运算符都可重载,只能重载C++中已经存在的运算符
  2. 重载之后运算符的优先级和结合性都不会变
  3. 一般来将,重载的功能应与原有功能类似,不能改变原运算符的操作对象的个数。
    至少要有一个操作对象自定义类型
  4. 这些操作符无法被重载
    类属关系运算符 .
    成员指针运算符 .*
    作用域分辨符 ::
    三目运算符 ?:
  5. 可重载为类的非静态成员函数非成员函数
  6. 重载的一般语法
    //重载为类的成员函数
    返回类型 类名::operator 运算符(形参表)
    {
    函数体
    }
    //重载为非成员函数
    返回类型 operator 运算符(形参表)
    {
    函数体
    }

2.3.1 运算符重载为成员函数(非静态)

2.3.1.1 双目运算符

//双目运算符等价
oprd1 B oprd2 //等价于下一行表达式
oprd1.operator B(oprd2)
//例
A+B
A.operator +(B)
/*
由以上等价可知,要重载“+”为A所属类的成员函数,而不是B的。
形参类型应该为B所属类型
*/

现在再看看2.2中的重载函数,一目了然

Complex Complex::operator+(const Complex &c2) const
{
return Complex(real + c2.real, imag + c2.imag); //创建一个临时对象作为返回值,这里调用了Complex类的构造函数
}
Complex Complex::operator-(const Complex &c2) const
{
return Complex(real - c2.real, imag - c2.imag); //创建一个临时对象作为返回值,这里调用了Complex类的构造函数
}

有些运算符无法重载为成员函数,如二元运算符的左操作数不是对象,或者是不能有我们重载运算符的对象。
例:支持
a+5.0但不支持5.0+a

2.3.1.2 单目运算符(前置)

//前置单目运算符等价
B oprd //等价于下一行表达式
oprd.operator B()
//例
++a
a.operator ++()

函数应被重载为oprd所属类成员函数无形参

2.3.1.3 单目运算符(后置)

//后置单目运算符等价
oprd B //等价于下一行表达式
oprd.operator ++(0) 或
oprd.operator ++(int)//函数原型
//例
x++
x.operator ++(0) 或
x.operator ++(int)//函数原型

函数应被重载为oprd所属类成员函数,且有一个int类型形参

2.3.1.4 例8-2 重载前置++和后置++为时钟类成员函数

注意两种重载返回值的区别,前置++返回值是左值(引用),后置++返回值是右值

/*
8-2 P311
将单目运算符“++”重载为成员函数的形式
*/
#include <iostream>
using namespace std;
class Clock
{ //时钟类定义
public:
Clock(int hour = 0, int minute = 0, int second = 0);
void showTime() const;
Clock &operator++(); //前置单目运算符重载
Clock operator++(int); //后置单目运算符重载
private:
int hour, minute, second;
}; Clock::Clock(int hour, int minute, int second)
{
if (0 <= hour && hour < 24 && 0 <= minute && minute < 60 && 0 <= second && second < 60)
{
this->hour = hour;
this->minute = minute;
this->second = second;
}
else
cout << "Time error!" << endl;
}
void Clock::showTime() const
{ //显示时间
cout << hour << ":" << minute << ":" << second << endl;
}
Clock &Clock::operator++()
{
second++;
if (second >= 60)
{
second -= 60;
minute++;
if (minute >= 60)
{
minute -= 60;
hour = (hour + 1) % 24;
}
}
return *this;
}
Clock Clock::operator++(int)
{ //注意形参表中的整型参数
Clock old = *this;
++(*this); //调用前置“++”运算符
return old;
}
int main()
{
Clock myClock(23, 59, 59);
cout << "First time output: ";
myClock.showTime();
cout << "Show myClock++: ";
(myClock++).showTime();
cout << "Show ++myClock: ";
(++myClock).showTime();
return 0;
}

运行结果:

First time output: 23:59:59
Show myClock++: 23:59:59
Show ++myClock: 0:0:1

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

规则:

  1. 函数形参从左到右,表示各操作数
  2. 参数个数==原操作数个数(后置++,--除外)
  3. 至少有一个自定义类型的参数
  4. 后置单目运算符++和--的重载函数中,形参要增加一个int,不必写形参名
  5. 重载函数需要某类对象的私有成员,可将该重载函数声明为该类的友元函数

2.3.2.1 等价

//双目运算符B
oprd1 B oprd2
operator B(oprd1,oprd2)
//例
a+b
operator +(a,b) //前置单目运算符
B oprd
operator B(oprd)
//例
++b
operator ++(b) //后置单目运算符
oprd B
operator B(oprd,0)
//例
b++
operator ++(b,0)

2.3.2.2 例8-3 重载Complex的加减法和“<<”运算符为非成员函数

cout 是 ostream 类的对象,是一个全局变量。ostream 类和 cout 在头文件 <iostream> 中声明。

/*
8-3 P313
以非成员函数形式重载Complex的加减法运算和“<<”运算符
*/
#include <iostream>
using namespace std;
class Complex
{
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
friend Complex operator+(const Complex &c1, const Complex &c2);
friend Complex operator-(const Complex &c1, const Complex &c2);
friend ostream &operator<<(ostream &out, const Complex &c); private:
double real; //复数实部
double imag; //复数虚部
};
Complex operator+(const Complex &c1, const Complex &c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
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.real << ", " << c.imag << ")";
return out;
}
int main()
{
Complex c1(5, 4), c2(2, 10), c3;
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
c3 = c1 - c2; //使用重载运算符完成复数减法
cout << "c3 = c1 - c2 = " << c3 << endl;
c3 = c1 + c2; //使用重载运算符完成复数加法
cout << "c3 = c1 + c2 = " << c3 << endl;
return 0;
}

参考:

C++语言程序设计(第5版),郑莉,清华大学

多态的概念和类型

【C++复习】第八章 多态性(1)(多态类型、运算符重载)的更多相关文章

  1. C# 类型运算符重载在类继承中的调用测试

    这是一篇晦涩难懂的片面的研究 一,简单的继承层次 class CA { } class CB : CA{ } class CC : CB{ } } void Test(CA oa){//CATest ...

  2. c++多态性及多态的内部实现(翁恺c++公开课[23-24])

    多态是在父类函数的前面加上 “virtual” 关键字,使子类与父类同名的函数产生一种联系: 多态会用到两个特性:向上造型.动态绑定 向上造型是指:拿一个子类对象当作父类来看待,比如下边代码中的子类E ...

  3. Newtonsoft.Json 处理多态类型的反序列化

    Newtonsoft.Json的序列化和反序列化很成熟也很好用, 最近在处理多态类型的反序列化中遇到了问题, 反序列化后只能到基类,而得不到也不能转换到子类.从网上查询了一番后,需要写一个创建类型的C ...

  4. C++多态性----运算符重载与虚函数

    一.多态性 ①概述:多态是指同样的消息被不同类型的对象接收时导致的不同行为. ②类型: 可以分为四类:重载多态.强制多态.包含多态.参数多态. ------------------------ --- ...

  5. C++ //多态 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 //动态多态:派生类和虚函数实现运行时多态

    1 //多态 2 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 3 //动态多态:派生类和虚函数实现运行时多态 4 5 //静态多态和动态多态的区别 6 //静态多态的函数地址早 ...

  6. PHP中的运算符---位运算符、递增递减运算符、三元运算符、字符串运算符、数组运算符、类型运算符、错误控制运算符

    1.位运算符 位运算符用来对整型数的指定位进行置位,如果被操作数是字符串,则对该字符串的ASCII码值进行操作. 运算类型 运算符 举例 结果 按位与 & $a & $b 将$a 与 ...

  7. php面向对象 封装继承多态 接口、重载、抽象类、最终类总结

    1.面向对象 封装继承多态  接口.重载.抽象类.最终类 面向对象 封装继承多态  首先,在解释面向对象之前先解释下什么是面向对象? [面向对象]1.什么是类? 具有相同属性(特征)和方法(行为)的一 ...

  8. C++ 类型转化(运算符重载函数)和基本运算符重载(自增自减)

    类型转化(运算符重载函数) 用转换构造函数可以将一个指定类型的数据转换为类的对象.但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据).在C ...

  9. Python3 学习笔记之 类型/运算符

    类型/运算符: 类型: 整数 字符串 浮点数 布尔类型 类型转换: 检查类型: 算术操作符: 逻辑操作符: 优先级:

  10. C++运算符重载复习

    本人理解运算符重载实质 就类似函数重载   运算符重载都可以写成一个函数 里面传入参数 来调用 运算符重载不是必须的 但是重载后会方便很多. 小例子 一个类实现 ++  和+某个数重载 大于号重载  ...

随机推荐

  1. 重置Beyond Compare 4试用时间

    重置Beyond Compare 4试用时间 什么是Beyound Compare Beyond Compare是一套由Scooter Software推出的文件比较工具.主要用途是对比两个文件夹或者 ...

  2. 第一天 python环境变量安装(2.7)

    一.什么是python Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. Python 的设计简单,易学,免费,开源,面向对象,可拓展性 Python 是一种解释型语言: ...

  3. python 操作配置文件(configparser模块)

    用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser 配置文件信息: [DEFAULT] ServerAliveInterval = 45 Com ...

  4. vue学习 第二天 CSS基础

    CSS: 层叠样式表  ( Cascading Style Sheets ) 的简称 1.css简介 1)也是一种标记语言 2)主要用来设置html页面中,标签的样式. 3)css美化了html页面, ...

  5. FileChannel 数据传输(文件拷贝)

    import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import ...

  6. mysql随机返回一条数据

    select * from biz_question ORDER BY RAND(id) LIMIT 1; SELECT * FROM biz_question AS t1 JOIN (SELECT ...

  7. Excel列动态导出

    列动态导出的Excel,大致整了一下,没有过多深入. 1.首先定义列样式 package a.b.common.util.excel.stream; import java.util.Objects; ...

  8. ubuntu18.04 安装cython_bbox

    方式一: pip install cython_bbox 方式二:下载地址 https://github.com/samson-wang/cython_bbox 解压后并cd cython_bbox- ...

  9. python经典例题

    [程序1] 题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? num_list=[] cou=0 for i in range(1,5): for j in rang ...

  10. taskkill报taskkill不是内部或者外部命令,也不是可运行程序

    转载一下处理这个'taskkill报taskkill不是内部或者外部命令,也不是可运行程序' 的问题:https://blog.csdn.net/wangying_2016/article/detai ...