第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例.

Slides:



Advertisements
Similar presentations
第11章 使用类.
Advertisements

第九章 排序 插入排序 交换排序 选择排序 归并排序 基数排序.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
C++程序设计 王希 图书馆三楼办公室.
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
資料大樓 --談指標與陣列 綠園.
函數(一) 自訂函數、遞迴函數 綠園.
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
C++程序设计 第二讲 清华大学软件学院.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
第七章 搜索结构 静态搜索结构 二叉搜索树 AVL树.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
Operator Overloading; String and Array Objects
西安交通大学 计算机教学实验中心 大学C++程序设计教程 西安交通大学 计算机教学实验中心
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Object-Oriented Programming in C++ 第一章 C++的初步知识
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
前處理指令可以要求前處理器 (preprocessor) 在程式編譯之前,先進行加入其它檔案的內容、文字取代以及選擇性編譯等工作。
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
第7章 编译预处理 本章要求: 本章重点: 本章难点: 掌握用#define定义无参数宏和带有参数宏定义和调用方法;
计算机网络讲义 第5章 批量数据处理—数组 一维数组 排序和查找 二维数组 字符串.
6 使用者函數 6.1 函數定義 宣告函數 呼叫函數 呼叫多個函數 6-6
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
第四章 小技巧.
类类型 C++支持的内置类型和操作,如 int i=10; i=i%6; i=i+4;
谭浩强 编著 中国高等院校计算机基础教育课程体系规划教材 C++程序设计.
計數式重複敘述 for 迴圈 P
$10 可空类型.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
Chapter4 Arrays and Matrices
第三章 链表 单链表 (Singly Linked List) 循环链表 (Circular List) 多项式及其相加
第七章 操作符重载 胡昊 南京大学计算机系软件所.
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
Classes (2) Lecture 7.
第十三讲 文件流与 输出输入重载.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
常宝宝 北京大学计算机科学与技术系 数据结构(三) 常宝宝 北京大学计算机科学与技术系
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
Chapter 2 & Chapter 3.
程式結構&語法.
第九章 排序 概述 插入排序 快速排序 交换排序(起泡排序) 选择排序 归并排序.
Oop8 function函式.
第四章 栈和队列 栈 ( Stack ) 队列 ( Queue ) 优先队列 (Priority Queue) 小结.
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
第三章 数据抽象.
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
Object-Oriented Programming in C++ 第二章 类和对象
C++大学基础教程 第10章 运算符重载 北京科技大学 2019/5/7 北京科技大学.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++程序设计 吉林大学计算机科学与技术(软件)学院.
C++程序设计基础 主讲人:谢昕 华东交通大学信息工程学院 第十~十二讲 多态性和虚函数 2005年春季学期.
第九章 物件導向-進階.
第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
挑戰C++程式語言 ──第9章 函數.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
第 4 章 类的高级部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本章主題 C++的程式結構 資料型態與宣告 算術運算 簡易的輸入輸出指令 程式編譯(Compile)的過程與原理.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
Presentation transcript:

第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例

什么是运算符重载 使系统内置的运算符可以用于类类型 例如:+ 运算符能够实现2个对象间的加。例如:类A的对象a1、a2、a3,希望:

问题的提出 把某些事交给系统去做,用户只要知道相加就可 扩充运算符的功能 增强了C++ 语言的可扩充性 使用户定义的类更像系统的内置类型

运算符重载的限制 不是所有的运算符都能重载 重载不能改变运算符的优先级和结合性 重载不能改变运算符的操作数个数 不能创建新的运算符

可以重载的运算符 +         -         *         /         %         ^         &        |     ~        !         =         <        >        +=        -=        *=     /=        %=        ^=        &=        |=        <<        >>        >>=     <<=       ==        !=        <=        >=        &&        ||        ++     --        ->*        ,        ->       []        ()        new       delete     new[]     delete[]

不能重载的运算符   .        .*        ::        ?:        sizeof

第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例

运算符重载的方法 运算符重载就是写一个函数解释某个运算符在某个类中的含义 要使得系统能自动找到重载的这个函数,函数名必须要体现出和某个被重载的运算符的联系。 C++中规定,重载函数名为 operator@ 其中,@为要重载的运算符。如要重载“+”运算符,该重载函数名为operator+。要重载赋值运算符,函数名为operator=。

函数原型 运算符的重载不能改变运算符的运算对象数。因此,重载函数的形式参数个数(包括成员函数的隐式指针this)与运算符的运算对象数相同 运算符重载可以重载成成员函数也可以重载成全局函数实现。重载成全局函数时,最好把此函数设为友员函数 如果作为类的成员函数,它的形式参数个数比运算符的运算对象数少1。这是因为成员函数有一个隐含的参数this。在C++中,把隐含参数this作为运算符的第一个参数。 当把一个一元运算符重载成成员函数时,该函数没有形式参数。 把一个二元运算符重载成成员函数时,该函数只有一个形式参数,就是右操作数,当前对象是左操作数。

重载实例 为rational类增加“+”和“*”以及比较的重载函数,用以替换现有的add和multi函数

方案一:重载成成员函数 class Rational { private: int num; int den; void ReductFraction(); public: Rational(int n = 0, int d = 1) { num = n; den = d;} Rational operator+(const Rational &r1) const; Rational operator*(const Rational &r1) const; bool operator<(const Rational &r1) const; bool operator==(const Rational &r1) const; bool operator>(const Rational &r1) const; bool operator<=(const Rational &r1) const; bool operator>=(const Rational &r1) const; bool operator!=(const Rational &r1) const; void display() { cout << num << '/' << den; } }

函数实现 Rational Rational::operator+(const Rational &r1) const { Rational tmp; tmp.num = num * r1.den + r1.num * den; tmp.den = den * r1.den; tmp.ReductFraction(); return tmp; } Rational Rational::operator*(const Rational &r1) const { Rational tmp; tmp.num = num * r1.num;

bool Rational::operator<(const Rational &r1) const { return num * r1.den < den * r1.num; } bool Rational::operator==(const Rational &r1) const { return num == r1.num && den == r1.den;} bool Rational::operator>(const Rational &r1) const { return num * r1.den > den * r1.num; } bool Rational::operator<=(const Rational &r1) const { return num * r1.den <= den * r1.num; } bool Rational::operator>=(const Rational &r1) const { return num * r1.den >= den * r1.num; } bool Rational::operator!=(const Rational &r1) const { return !(*this == r1);}

方案二:重载成友员函数 class Rational { friend Rational operator+(const Rational &r1, const Rational &r2); friend Rational operator*(const Rational &r1 , const Rational &r2); friend bool operator<(const Rational &r1 , const Rational &r2) ; friend bool operator==(const Rational &r1 , const Rational &r2); friend bool operator>(const Rational &r1 , const Rational &r2) ; friend bool operator<=(const Rational &r1 , const Rational &r2); friend bool operator>=(const Rational &r1 , const Rational &r2); friend bool operator!=(const Rational &r1 , const Rational &r2) ; private: int num; int den; void ReductFraction(); public: Rational(int n = 0, int d = 1) { num = n; den = d;} void display() { cout << num << '/' << den;} };

函数的实现 Rational operator+(const Rational &r1, const Rational &r2) { Rational tmp; tmp.num = r1.num * r2.den + r2.num * r1.den; tmp.den = r1.den * r2.den; tmp.ReductFraction(); return tmp; } Rational operator*(const Rational &r1, const Rational &r2) tmp.num = r1.num * r2.num; 其他函数实现略

重载后有理数类的使用 int main() { Rational r1(1,6), r2(1,6), r3; r3 = r1 + r2; r1.display(); cout << " + "; r2.display(); cout << " = "; r3.display(); cout << endl; r3 = r1 * r2; r1.display(); cout << " * "; r2.display(); return 0; }

全局函数 vs成员函数 大多数运算符都可以重载成成员函数或全局函数。 赋值(=)、下标([])函数调用(())和成员访问(->)必须重载成成员函数。 具有赋值意义的运算符,如复合的赋值运算符以及++和--,不一定非要定义为成员函数,但最好定义为成员函数。 具有两个运算对象的运算符最好重载为全局函数,这样可以使得应用更加灵活。如果把加运算定义成全局函数,r是有理数类的对象,则2+r是一个合法的表达式。

第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例

几个特殊的运算符的重载 赋值运算符 下标运算符 函数调用运算符 ++和—运算符的重载 重载函数的原型设计考虑 输入输出运算符重载

赋值运算符 对任一类,如果用户没有自定义赋值运算符函数,那么系统为其生成一个缺省的赋值运算符函数,在对应的数据成员间赋值。 一般情况下,这个缺省的赋值运算符重载函数能满足用户的需求。但是,当类含有类型为指针的数据成员时,可能会带来一些麻烦。

对DoubleArray类对象执行 array1 = array2的问题 会引起内存泄漏 使这两个数组的元素存放于同一块空间中 当这两个对象析构时,先析构的对象会释放存储数组元素的空间。而当后一个对象析构时,无法释放存放数组元素的空间

赋值运算符“=”的原型 赋值运算符只能重载成成员函数 函数原型: X &X::operator=(const X &source) { // 赋值过程 } 一旦创建了对象x1, x2, 可以用 x1 = x2赋值。

DoubleArray类的 赋值运算符重载函数 DoubleArray &DoubleArray::operator= (const DoubleArray &right) { if (this == &right) return *this; delete [ ] storage; low = right.low; high = right.high; storage = new double[high - low + 1]; for (int i=0; i <= high - low; ++i) storage[i] = right.storage[i]; //复制数组元素 return *this; }

赋值运算符重载要点 一般来讲,需要自定义拷贝构造函数的类也需要自定义赋值运算符重载函数。 在赋值运算符重载函数中,已经将参数的值赋值给了当前对象,那为什么还需要返回值呢?记住,在C++中,赋值是一个运算,它可以形成一个表达式,而该表达式的结果值就是赋给左边的对象的值。因此,赋值运算符重载函数必须返回赋给左边的对象值。

赋值运算符重载和拷贝构造函数 一般来讲,需要拷贝构造函数的类也需要重载赋值运算符 定义对象时给对象赋初值调用的是拷贝构造函数 程序的语句部分中的赋值语句调用的是赋值运算符重载函数

几个特殊的运算符的重载 赋值运算符 下标运算符 函数调用运算符 ++和—运算符的重载 重载函数的原型设计考虑 输入输出运算符重载

下标运算符重载 能否象普通的数组那样通过下标运算操作DoubleArray类的对象,这样可以使DoubleArray类更像一个功能内置的数组。 可以通过重载下标运算符([])来实现 下标运算符是二元运算符,第一个运算数是数组名,第二个运算数是下标值 下标运算符必须重载成成员函数

DoubleArray类的[ ]重载 double & DoubleArray::operator[](int index) { if (index < low || index > high) {cout << "下标越界"; exit(-1); } return storage[index - low]; }

DoubleArray类的使用 定义:DoubleArray array(20, 30); 数组输入: for (i=20; i<=30; ++i) { cout << "请输入第" << i << "个元素:"; cin >> array[i]; } 数组输出: for (i=20; i<=30; ++i) cout << array[i] << '\t';

几个特殊的运算符的重载 赋值运算符 下标运算符 函数调用运算符 ++和—运算符的重载 重载函数的原型设计考虑 输入输出运算符重载

函数调用运算符 函数调用运算符()是一个二元运算符。它的第一个运算对象是函数名,第二个参数是形式参数表。运算的结果是函数的返回值。 一个类重载了函数调用运算符,就可以把这个类的对象当做函数来使用

函数调用运算符重载 函数调用运算符必须重载成成员函数 函数调用运算符重载函数的原型为 函数的返回值 operator() (形式参数表);

函数调用运算符重载实例 在DoubleArray类增加一个功能:取数组中的一部分元素形成一个新的数组 例如,在一个下标范围为10到20的数组arr中取出下标为第12到15的元素,形成一个下标范围为2到5的数组存放在数组arr1中,可以调用 arr1 = arr(12, 15, 2)。

DoubleArray operator()(int start, int end, int lh) { if (start > end || start < low || end > high ) { cout << "下标越界"; exit(-1); } DoubleArray tmp(lh, lh + end - start); for (int i = 0; i < end - start + 1; ++i) tmp.storage[i] = storage[start + i - low]; return tmp; }

几个特殊的运算符的重载 赋值运算符 下标运算符 函数调用运算符 ++和—运算符的重载 重载函数的原型设计考虑 输入输出运算符重载

“++”和“--”重载 ++、- -:是一元操作符 这两个操作符可以是前缀,也可以是后缀。而且前缀和后缀的含义是有区别的。所以,必须有两个重载函数。 问题:两个重载函数有相同的原型 区分方法: 前缀:一元操作符。 后缀:二元操作符。

“++”和“--”重载 cont. 成员函数重载 ++ob重载为:ob.operator++() ob-- 重载为:ob.operator--(int) 友元函数重载 ++ob重载为:operator++(X &ob) ob--重载为:operator--(X &ob, int) 调用时,参数int一般传递给值0。

++、--重载实例 设计一个会报警的计数器类。该计数器从0开始计数,当到达预先设定好的报警值时,计数器会发出报警消息,计数器的值不再增加。

类定义 class Counter { int value; //计数器的值 int alarm; //报警值 public: Counter(int a) {value = 0; alarm = a;} Counter & operator++(); //前缀的++重载 Counter operator++(int); //后缀的++重载 void print() {cout << value << endl; } };

类实现 Counter & Counter::operator++() { if (value == alarm) cout << "已超过报警值\n"; else { ++value; if (value == alarm) cout << "已到达报警值\n"; } return *this; Counter Counter::operator++(int x) { Counter tmp = *this; //保存对象修改前的状态 if (value == alarm) cout << "已超过报警值\n"; return tmp; //返回修改前的状态

类的使用 int main() { Counter cnt(3); //定义一个Counter类的对象,报警值为3 cnt.print(); /显示对象的当前值,此时输出为0 ++cnt; cnt.print(); // 此时输出为1 (++cnt).print(); //调用前缀的++,输出2 (cnt++).print(); //调用后缀的++,当前对象的value已经 //加1,报警。但输出的是2 cnt.print(); //输出值为3 return 0; }

几个特殊的运算符的重载 赋值运算符 下标运算符 函数调用运算符 ++和—运算符的重载 重载函数的原型设计考虑 输入输出运算符重载

重载函数的原型设计考虑 参数设计 返回值的类型设计 对于任何函数的参数,如果仅需要从参数中读,而不改变它,一般用const引用来传递。 只有会修改左值参数的运算符,如赋值运算符,左值参数不是常量,所以用地址传递 返回值的类型设计 运算符的结果产生一个新值,就需要产生一个作为返回值的新对象 对于逻辑运算符,人们希望至少得到一个int或bool的返回值 所有的赋值运算符(如,=,+=等)均改变左值,应该能够返回一个刚刚改变了的左值的非常量引用

值返回时的优化 在返回一个对象时,通常有两种写法。如某函数返回一个Rational类的对象,它的值为两个参数的成员对应相加。它的两种写法为 return Rational( left.num + right.num, left.den + right.den); Rational tmp; tmp.num = left.num + right.num; tmp.den = left.den + right.den; return tmp;

两种写法的比较 前者的意思是“创建一个临时对象,并返回它” 。它只调用了一次构造函数。 而后者,先创建了一个对象tmp,这将调用构造函数,然后对tmp赋值,最后返回tmp。而在返回tmp时,又要创建一个临时对象,并调用拷贝构造函数用tmp对它进行初始化。在函数执行结束时,还要调用析构函数析构tmp。

几个特殊的运算符的重载 赋值运算符 下标运算符 函数调用运算符 ++和—运算符的重载 重载函数的原型设计考虑 输入输出运算符重载

输入输出运算符重载 输入输出运算符必须被重载成全局函数。 输出运算符的重载 输入运算符的重载 借助于流插入运算符(>>)和流提取运算符(<<)输入和输出用户自定义类的对象 输入输出运算符必须被重载成全局函数。 输出运算符的重载 输入运算符的重载

输出重载函数的原型 ostream & operator<<(ostream & os, const ClassType &obj) { os << 要输出的内容; return os; }

实例 为Rational类重载输出 ostream& operator<<(ostream &os, const Rational& obj) // 输出重载函数 { os << obj.num << '/' << obj.den; return os; } 如定义: Rational r(2,6); 执行cout << r;的结果是 1/3。

输入输出运算符重载 输入输出运算符必须被重载成全局函数。 输出运算符的重载 输入运算符的重载 借助于流插入运算符(>>)和流提取运算符(<<)输入和输出用户自定义类的对象 输入输出运算符必须被重载成全局函数。 输出运算符的重载 输入运算符的重载

输入重载函数的原型 istream & operator>>(istream & is, ClassType &obj) return is; }

实例 为Rational类重载输入 istream& operator>>(istream &in, Rational& obj) // 输入重载函数 { in >> obj.num >> obj.den; obj.ReductFraction(); return in; } 如定义:Rational r; 可以用cin >> r 从键盘输入r的数据。如输入为:1 3 执行cout << r;的结果是 1/3。

第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例

类型转换--系统预定义类型间的转换 隐式类型转换 ※ 赋值时 ※ 运算时 显式类型转换 ※ 强制转换法:(类型名)表达式 ※ 函数法:类型名(表达式)

自定义类型转换运算符 类类型能否和其他的类类型或内置类型互相转换? 内置类型之所以能互相转换是因为系统预先制定了转换的规则,并写好了完成转换的程序。 类类型与其它类类型或内置类型之间如何转换,编译器预先无法知道。类的设计者必须定义转换的方法。

类型转换 内置类型到类类型的转换 类类型到其它类型的转换

内置类型到类类型的转换 利用构造函数进行转换。 例如,对于Rational类的对象r,可以执行r=2。 此时,编译器隐式地调用Rational的构造函数,传给它一个参数2。构造函数将构造出一个num=2,den= 1的Rational类的对象,并将它赋给r。

explicit构造函数 任何单参数的构造函数都可以被编译器用来执行隐式转换,即把内置类型转换成对应的类类型。 在某些情况下,隐式转换是不受欢迎的。 将单参数的构造函数定义为explicit,将告诉编译器不允许执行隐式转换。 如将Ratioanal类的构造函数定义成 explicit Rational(int n1 = 0, int n2 = 1) 则对于Rational类的对象r1和r2,执行 r1 = 2 + r2;编译器就会报错

类型转换 内置类型到类类型的转换 类类型到其它类型的转换

类类型到内置类型或其他类类型的转换 可以通过类型转换函数实现 类型转换函数必须重载成成员函数 类型转换函数的格式 operator 目标类型名 ( ) const { … return (结果为目标类型的表达式); } 类型转换函数的特点 无参数,无返回值 是const函数

Rational类到double的转换 转换函数的定义: operator double () const { return (double(num)/den);} 有了这个函数,我们可以将一个Rational类的对象r赋给一个double类型的变量x。如r的值为(1,3),经过赋值x = r后,x的值为0.333333

经过运算符重载后的Rational类 class Rational { friend istream& operator>>(istream &in, Rational& obj); friend ostream& operator<<(ostream &os, const Rational& obj); friend Rational operator+(const Rational &r1, const Rational &r2); friend Rational operator*(const Rational &r1, const Rational &r2); private: int num; int den; void ReductFraction(); public: Rational(int n = 0, int d = 1) { num = n; den = d;} operator double () const { return (double(num)/den);} };

Rational类的使用 输入r1: 1 3 输入r2: 2 6 #include <iostream.h> 1/3+1/3 = 2/3 1/3*1/3 = 1/9 (r1 + r2) * r3的值为2/27 5.5 - r1的值为:5.16667 1/3 #include <iostream.h> #include "Rational.h" int main() { Rational r1, r2, r3, r4; double x; cout << "输入r1: "; cin >> r1; cout << "输入r2: "; cin >> r2; r3 = r1 + r2; cout << r1 << '+' << r2 << " = " << r3 << endl; r3 = r1 * r2; cout << r1 << '*' << r2 << " = " << r3 << endl; r4 = (r1 + r2) * r3; cout << "(r1 + r2) * r3的值为:" << r4 << endl; x = 5.5 - r1; cout << "5.5 - r1的值为:" << x << endl; cout << (r1 < r2 ? r1 : r2) << endl; return 0; }

第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例

运算符重载实例 完善DoubleArray类

DoubleArray.h #ifndef _array_h #define _array_h #include <iostream.h> class DoubleArray{ friend ostream &operator<<(ostream &os, const DoubleArray &obj); friend istream &operator>>(istream &is, DoubleArray &obj); friend bool operator==(const DoubleArray &obj1, const DoubleArray &obj2); private: int low; int high; double *storage;

public: DoubleArray(int lh = 0, int rh = 0):low(lh), high(rh) { storage = new double [high - low + 1]; } DoubleArray(const DoubleArray &arr); DoubleArray &operator=(const DoubleArray &right); double & operator[](int index); const double & operator[](int index) const; DoubleArray operator()(int start, int end, int lh); ~DoubleArray() {delete [] storage; } }; #endif

DoubleArray.cpp //文件名:DoubleArray.cpp //DoubleArray类的实现 #include <cassert> #include "DoubleArray.h“ DoubleArray::DoubleArray(const DoubleArray &arr) { low = arr.low; high = arr.high; storage = new double [high - low + 1]; for (int i = 0; i < high -low + 1; ++i) storage[i] = arr.storage[i]; }

operator= DoubleArray &DoubleArray::operator= (const DoubleArray & a) { if (this == &a) return *this; delete [] storage; low = a.low; high = a.high; storage = new double[high - low + 1]; for (int i=0; i <= high - low; ++i) storage[i] = a.storage[i]; return *this; }

operator[] double & DoubleArray::operator[](int index) { assert(index >= low && index <= high); return storage[index - low]; } const double & DoubleArray::operator[] (int index) const

operator<< ostream &operator<<(ostream &os, const DoubleArray &obj) { os << "数组内容为:\n"; for (int i=obj.low; i<=obj.high; ++i) os << obj[i] << '\t'; os << endl; return os; }

operator>> istream &operator>>( istream &is, DoubleArray &obj) { cout << "请输入数组元素[" << obj.low << ", " << obj.high << "]:\n"; for (int i=obj.low; i<=obj.high ; ++i) is >> obj[i] ; return is; }

operator== bool operator==(const DoubleArray &obj1, const DoubleArray &obj2) { if (obj1.low != obj2.low || obj1.high != obj2.high) return false; for (int i = obj1.low; i<=obj1.high; ++i) if (obj1[i] != obj2[i]) return false; return true; }

operator() DoubleArray DoubleArray::operator() (int start, int end, int lh) { assert (start <= end && start >= low && end <= high ); DoubleArray tmp(lh, lh + end - start); for (int i = 0; i < end - start + 1; ++i) tmp.storage[i] = storage[start + i - low]; return tmp; }

Main函数 int main() { DoubleArray array1(20,30), array2; cin >> array1; cout << "array1 "; cout << array1; array2 = array1; cout << "执行 array2 = array1, array2 " << array2; cout << "array1 == array2 是 " << ((array1 == array2) ? "true" : "false") << endl; array2[25] = 0; cout << "执行array[25] = 0后, array1 == array2 是 " array2 = array1(22, 25, 2); cout << "执行array2 = array1(22, 25, 2)后, array2 的值为: " << array2; return 0; }

执行结果 请输入数组元素[20,30]: 1 2 3 4 5 6 7 8 9 10 11 array1的内容为: 1 2 3 4 5 6 7 8 9 10 11 执行 array2 = array1,array2的内容为: array1 == array2是true 执行array2[25] = 0后,array1 == array2是false 执行array2 = array1(22, 25, 2)后, array2 的值为:3 4 5 6

小结 运算符重载的作用 如何选择用成员函数或全局函数 如何写一个重载函数 介绍了一种区分++和—的前后缀应用的方法 通过运算符重载实现类类型和内置类型及其他类类型之间的转换

第12章 组合与继承 组合 继承 虚函数与多态性 纯虚函数与抽象类 多继承 面向对象设计范例

组合 组合就是把用户定义类的对象作为新类的数据成员 组合表示一种聚集关系,是一种部分和整体(is a part of)的关系 必须用初始化列表去初始化对象成员

组合实例 定义一个复数类,而复数的虚部和实部都用有理数表示

类定义 class Complex{ friend Complex operator+(Complex x, Complex y); friend istream& operator>>(istream &is, Complex &obj); friend ostream& operator<<(ostream &os, const Complex &obj); Rational real; //实部 Rational imag; //虚部 public: Complex(int r1 = 0, int r2 = 1, int i1= 0, int i2 = 1): real(r1, r2), imag(i1, i2) {} };

成员函数的实现 Complex operator+(Complex x, Complex y) { Complex tmp; //利用Rational类的加法重载函数完成实部和虚部的相加 tmp.real = x.real + y.real; tmp.imag = x.imag + y.imag; return tmp; }

istream& operator>>(istream &is, Complex &obj) { cout << "请输入实部:"; is >> obj.real; //利用Rational类的输入重载实现实部的输入 cout << "请输入虚部:"; is >> obj.imag; //利用Rational类的输入重载实现虚部的输入 return is; } ostream& operator<<(ostream &os, const Complex &obj) { //利用Rational类的输出重载实现实部和虚部的输出 cout << '(' << obj.real << " + " << obj.imag << "i" << ')'; return os;

复数类的使用 int main() {Complex x1,x2,x3; cout << "请输入x1:\n"; cin >> x1; cout << "请输入x2: \n"; cin >> x2; x3 = x1 + x2; cout << x1 << " + " << x2 << " =   " << x3 << endl; return 0; }