刘胥影 liuxy@seu.edu.cn http://cse.seu.edu.cn/people/xyliu/ 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 liuxy@seu.edu.cn http://cse.seu.edu.cn/people/xyliu/ 东南大学计算机学院.

Slides:



Advertisements
Similar presentations
第 2 章 初探 C++.
Advertisements

第11章 使用类.
第九章 排序 插入排序 交换排序 选择排序 归并排序 基数排序.
程設一.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
流类库与输入/输出 输入/输出标准流类 文件流类 串流类 输入/输出成员函数 用户自定义类型的输入/输出.
C# 程式設計 第一部分 第1-4章 C# 程式設計 - 南華大學資管系.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。
資料大樓 --談指標與陣列 綠園.
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
Chap 3 堆疊與佇列 Stack and Queue.
Scope & Lifetime 前言 Local Scope Global Functions & Objects
第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
第七章 搜索结构 静态搜索结构 二叉搜索树 AVL树.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
Classes: A Deeper Look, Part 1
第六章 继承性和派生类 胡昊 南京大学计算机系软件所.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
Object-Oriented Programming:
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++語言的動態記憶體配置
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
程式撰寫流程.
第6章 基本的IDL到C++的映射 3.2 简介 从IDL到C++的映射必须具备下面的条件 (1) 映射应该很直观,并且很容易使用
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
类类型 C++支持的内置类型和操作,如 int i=10; i=i%6; i=i+4;
谭浩强 编著 中国高等院校计算机基础教育课程体系规划教材 C++程序设计.
C/C++/Java 哪些值不是头等程序对象
$10 可空类型.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
Chapter4 Arrays and Matrices
第七章 操作符重载 胡昊 南京大学计算机系软件所.
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
第 14 章 輸出與輸入.
Classes (2) Lecture 7.
第十三讲 文件流与 输出输入重载.
第14章 输入输出与文件 输入输出是指程序与外部设备交换信息 C++把输入输出看成是一个数据流 输入流:外围设备流向内存的数据
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
Chapter 2 & Chapter 3.
C#程序设计基础 $3 成员、变量和常量.
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
第三章 数据抽象.
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
C++大学基础教程 第10章 运算符重载 北京科技大学 2019/5/7 北京科技大学.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
面向对象技术 练习 ffh.
C++程序设计基础 主讲人:谢昕 华东交通大学信息工程学院 第十~十二讲 多态性和虚函数 2005年春季学期.
C/C++基礎程式設計班 C++: 物件的使用、參考、重載函式 講師:林業峻 CSIE, NTU 3/28, 2015.
第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
第 4 章 类的高级部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本章主題 C++的程式結構 資料型態與宣告 算術運算 簡易的輸入輸出指令 程式編譯(Compile)的過程與原理.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
第9章 C++程序设计初步 9.1 C++的特点 9.2 最简单的C++程序 9.3 C++的输入输出 9.4 函数的重载
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
Presentation transcript:

刘胥影 liuxy@seu.edu.cn http://cse.seu.edu.cn/people/xyliu/ 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 liuxy@seu.edu.cn http://cse.seu.edu.cn/people/xyliu/ 东南大学计算机学院

运算符重载 11.1 Introduction 11.2 Fundamentals of Operator Overloading 11.3 Restrictions on Operator Overloading 11.4 Operator Functions as Class Members vs. Global Functions 11.5 Overloading Streaming Insertion and Stream Extraction Operators 11.6 Overloading Unary Operators 11.7 Overloading Binary Operators 11.8 Case Study: Array Class 11.9 Converting between Types 11.10 Case Study: String Class 11.11 Overloading ++ and -- 11.12 Case Study: A Date Class 11.13 Standard Library Class string 11.14 explicit Constructors (自学,了解)

运算符重载简介 重载函数(回顾) 运算符重载

重载函数(1) 重载函数(overloading) 回顾chap6 重载函数(overloading) 出现在相同作用域中的两个函数,具有相同的名字而形参表不 同,则称为重载函数,它们是不同的函数 形参个数不同 形参类型不同 成员函数可以有const版本的重载,供const对象调用 经常用于创建执行相似任务,但作用于不同数据类型的具有相 同名字的多个函数 square (int x); square (double x); int + int double +double 普通函数重载 运算符(函数)重载 东南大学计算机学院 11/21/2018

重载函数(2) 重载函数的匹配 根据实参的类型匹配 根据对象是否为const对象匹配 回顾chap6 重载函数的匹配 根据实参的类型匹配 根据对象是否为const对象匹配 考虑默认值: 若省略实参时,形式与另一个重载函数一致,会产生编译 错误 void f(int a); //调用形式:f(1) void f(int a, int b = 0); //调用形式:f(1,2) 重复声明 东南大学计算机学院 11/21/2018

重载函数(3) 函数重载VS.重复声明 若形参表相同而返回类型不同,则第二个声明错误 重复声明: 函数名、返回类型、形参表都相同 回顾chap6 回顾 函数重载VS.重复声明 重复声明: 函数名、返回类型、形参表都相同 若形参表相同而返回类型不同,则第二个声明错误 void f1(int a); void f1(int); typedef A B; void f2(A &); void f2(B &); void f3(int a, int b); void f3(int a, int b = 0); //默认实参不改变形参个数 重复声明 重复声明 重复声明 void f4(A); void f4(const A); void f5(A&); void f5(const A&); void A::f(); void A::f() const; 对副本操作,无法改变实参,因此效果一样 重复声明 传递实参,第一个可改变实参,第二个不可以改变实参,因此效果不一样 函数重载 函数重载 东南大学计算机学院 11/21/2018

operator+(double,double) 运算符重载(1) 运算符是一类特殊的函数 3 + 1 运算符重载 operator+(int,int) 重载的运算符函数 3.0 + 1.0 operator+(double,double) 东南大学计算机学院 11/21/2018

运算符重载(2) 运算符重载的必要性 代码清晰直观,符合人的习惯 根据语义,隐藏实现细节 一般来说,类对象使用运算符必须重载 不可能为用户自定义的类对象预先定义运算规则 函数调用 运算符 a.plus(b); a.minus(b); a = a + b; a = a – b; int x1, y1, a; double x2, y2, b; a = x1 + y1; //int b = x2 + y2; //double 运算符重载是C++可扩展性的一个重要方面 东南大学计算机学院 11/21/2018

运算符重载的基础知识 运算符函数 运算符重载方式 类对象的运算符重载

运算符函数 函数名:operator@ 需重载的运算符 + 运算符重载函数名 operator+()

运算符重载方式 以成员函数重载:non-static成员函数 运算符是对对象的操作,由对象调用 以全局函数重载:一般声明为友元 友元可以访问类的所有数据成员 Complex Complex::operator+(const Complex &) Complex a, b, c; c = a + b; 调用方式: Complex c = a.operator+(b) Complex operator+(const Complex &, const Complex &) 调用方式: Complex c = operator+(a, b) class Complex{ friend Complex operator+(……); …… } 东南大学计算机学院 11/21/2018

类对象的运算符重载 类对象使用运算符,必须重载该运算符 三个例外: 赋值运算符 = 取址(&) 逗号(,) 默认的逐个成员赋值(chap9) (&) 既是“取址”运算符,又是“按位与”运算符 只有当它是取址运算符时才能直接用于对象

运算符重载的限制

运算符重载的限制(1) 不能创造新的运算符 Appendix A 通过指针的成员指针 通过对象的成员指针 唯一的三元运算符

运算符重载的限制(2) 不能改变优先级、结合律 如果希望重载的运算符具有不同的优先级和结合律,只能使用( ) 东南大学计算机学院

运算符重载的限制(3) 不能改变元数 一元运算符 二元运算符 三元运算符:不可重载(?:) 元数:运算符的操作数的个数 一元运算符:+1, -1, !, … 二元运算符: a+b, a-b, a*b, a/b, … 三元运算符:?: 一元/二元运算符: + - * & 一元 正号 负号 指针 取址 二元 加 减 乘 按位与 重载哪个运算符由元数控制 东南大学计算机学院

运算符重载的限制(4) 不能改变对基本类型对象的操作语义 当只包含基本类型对象时,运算符重载不起作用 类库重载了基本类型对象的运算符 当只包含基本类型对象时,运算符重载不起作用 只有当用户自定义类型的对象为操作数时,运算符重载 才起作用 一元运算符:自定义类型的对象 二元运算符:(不考虑顺序) 自定义类型对象 +自定义类型对象 自定义类型对象 + 基本类型对象 东南大学计算机学院 11/21/2018

X 运算符重载的限制(5) 只能显式重载,不能自动实现 obj2 = obj1 + obj2 obj2 += obj1 已重载+ +=被重载 东南大学计算机学院 11/21/2018

内 容 回 顾 数据抽象和信息隐藏 运算符重载基础 信息隐藏 数据抽象 抽象数据类型ADT 重载函数和运算符重载 运算符重载的限制 不能改变对基本类型对象的操作语义 只有当用户自定义类型的对象为操作数时,运算符重载 才起作用 东南大学计算机学院 11/21/2018

内 容 回 顾 两种运算符函数 成员运算符函数:必须是non-static成员函数 全局运算符函数:一般为友元 东南大学计算机学院 11/21/2018

11.4 11.7 实现运算符函数: 类成员函数VS. 全局函数 重载一元运算符 重载二元运算符 两种运算符函数 两种运算符函数比较 一元运算符重载 二元运算符重载 重载可交换的运算符 东南大学计算机学院 11/21/2018

只能重载一元/二元运算符 二元运算符: 两个操作数 一元运算符: 一个操作数 a + b a – b a * b a / b + a a 二元运算符: 两个操作数 一元运算符: 一个操作数 左操作数 a + b a – b a * b a / b 右操作数 左操作数 + a a ! a a ++ a -- 东南大学计算机学院 11/21/2018

两种运算符函数(1) 成员运算符函数:non-static成员函数 运算符是对对象的操作,由对象调用 A a; 一元运算符 + a class A{ public: 返回值类型 operator+( ); }; a.operator+( ) 调用方式: + a 唯一的左操作数作为句柄对象调用成员运算符函数 左操作数必须为A类对象 东南大学计算机学院 11/21/2018

两种运算符函数(2) 成员函数实现一元运算符举例 左操作数必须为Complex类对象 Complex a(-3,-1); Complex b = + a; a = -3 – i b = 3 + i class Complex{ public: Complex operator+( ); private: double realPart; double imaginaryPart; }; Complex Complex::operator+( ) { Complex result(0,0); result.realPart = + realPart; result. imaginaryPart = + imaginaryPart; return result; } b = a.operator+( ) 调用方式: b = + a 左操作数必须为Complex类对象 东南大学计算机学院 11/21/2018

两种运算符函数(3) 成员运算符函数:non-static成员函数 二元运算符 调用方式: 左操作数:作为句柄对象调用成员运算符函数 A a; B b; 二元运算符 a + b class A{ public: 返回值类型 operator+(const B &); }; 调用方式: a.operator+( b ) a + b 左操作数:作为句柄对象调用成员运算符函数 右操作数:作为参数传递给成员运算符函数 左操作数必须为A类对象 东南大学计算机学院 11/21/2018

两种运算符函数(4) 成员函数实现二元运算符举例 左操作数必须为Complex类对象 Complex a(1,1); double b = 2.0; Complex c = a + b; a = 1 + i b = 2.0 c = 3 + i class Complex{ public: Complex operator+(double); private: double realPart; double imaginaryPart; }; Complex Complex::operator+(double b) { Complex result(0,0); result.realPart = realPart + b; result. imaginaryPart = imaginaryPart; return result; } c = a.operator+( b ) 调用方式: c = a + b 左操作数必须为Complex类对象 东南大学计算机学院 11/21/2018

Complex operator+( const Complex &) 两种运算符函数(5) 成员函数实现二元运算符举例 class Complex{ public: Complex operator+(double); private: double realPart; double imaginaryPart; }; Complex a(1,1); double b = 2.0; Complex c = a + b; //ok. c = a.operator+(b); Complex d = b + a; //error. 若以成员函数方式调用: d = b.operator+(a); 尝试调用double类的成员函数: Complex operator+( const Complex &) 内置类型不能修改! 东南大学计算机学院 11/21/2018

两种运算符函数(6) 成员运算符函数的限制 左操作数必须为自定义类A类对象 右操作数可以是任意类类型/基本类型对象 a + b + a 调用方式: a.operator+( ) a.operator+( b ) 左操作数必须为自定义类A类对象 关心操作数的顺序 右操作数可以是任意类类型/基本类型对象 左操作数是其他类/基本类对象时,必须使用全局运算符函数 东南大学计算机学院 11/21/2018

两种运算符函数(7) 全局运算符函数重载:一般声明为友元 友元可以访问类的所有数据成员 一元运算符 注意: 是全局函数,而不是类A的成员 A a; 一元运算符 + a 注意: 是全局函数,而不是类A的成员 class A{ friend 返回值类型 operator+( const A & ); }; 返回值类型 operator+(const A &){……} 调用方式: operator+( a ) + a 操作数作为参数传入全局运算符函数 参数必须为自定义类对象/引用(内置类型无法重定义运算符)

两种运算符函数(8) 全局函数实现一元运算符举例 Complex a(-3,-1); Complex b = + a; 调用方式: a = -3 – i b = 3 + i b = operator+(a) 调用方式: b = + a class Complex{ friend Complex operator+( const Complex & ); private: double realPart; double imaginaryPart; }; Complex operator+( const Complex & a){ Complex result(0,0); result.realPart = + a.realPart; result.imaginaryPart = + a.imaginaryPart; return result; } 东南大学计算机学院 11/21/2018

参数必须有一个自定义类对象/引用 (内置类型无法重定义运算符) 两种运算符函数(9) 全局运算符函数重载:一般声明为友元 A a; B b; 二元运算符 a + b class A{ friend 返回值类型 operator+(const A &, const B &); }; 注意: 是全局函数,而不是类A的成员 返回值类型 operator+(const A &, const B &){……} 调用方式: operator+(a, b) a + b 操作数作为参数传入全局运算符函数 参数必须有一个自定义类对象/引用 (内置类型无法重定义运算符) 11/21/2018

两种运算符函数(10) 全局函数实现二元运算符举例 Complex a(1,1); double b = 2.0; Complex c = a + b; a = 1 + i b = 2.0 c = 3 + i c = operator+(a,b) 调用方式: a + b class Complex{ friend Complex operator+(const Complex &, double); private: double realPart; double imaginaryPart; }; Complex operator+(const Complex & a, double b) { Complex result(0,0); result.realPart = a.realPart + b; result.imaginaryPart = a.imaginaryPart; return result; } 东南大学计算机学院 11/21/2018

两种运算符函数(11) 全局运算符函数不限制操作数的顺序 Complex a(1,1); double b = 2.0; Complex c = a + b; //ok. c = operator+(a, b); Complex d = b + a; //ok c = operator+(b, a); class Complex{ friend Complex operator+(const Complex &, double); friend Complex operator+(double, const Complex &); private: double realPart; double imaginaryPart; }; Complex operator+(double b, const Complex & a) { return operator+(a, b); } 全局运算符函数不限制操作数的顺序

两种运算符函数比较 必须作为成员函数重载的运算符 必须作为全局函数重载的运算符 其他运算符可以作为成员函数或全局函数进行重载 ()、[]、-> 任何赋值运算符 必须作为全局函数重载的运算符 流插入操作符(<<)和流提取运算符(>>) 可交换的运算符(指操作对象类型不同) 其他运算符可以作为成员函数或全局函数进行重载 用法一样,实现不同 东南大学计算机学院 11/21/2018

一元运算符重载(1) 成员运算符函数 全局运算符函数 non-static成员函数 没有参数 左操作数必须为本类对象 一般为友元函数 一个参数 参数必须是自定义类对象 + a a.operator+( ) operator+( a ) 东南大学计算机学院 11/21/2018

一元运算符重载(2) 判断String对象s是否为空字符串: 成员运算符重载 全局运算符重载 调用方式:s.operator!( ) class String { public: bool operator!() const; ... }; 调用方式:s.operator!( ) bool operator!( const String & ); 调用方式:operator!( s ) 东南大学计算机学院 11/21/2018

二元运算符重载(1) 成员运算符函数 全局运算符函数 一般为友元函数 non-static成员函数 两个参数 一个参数 左操作数必须为本类对象 一般为友元函数 两个参数 参数必须有一个自定义类对象 a + b a.operator+( b ) operator+(a, b) 东南大学计算机学院 11/21/2018

二元运算符重载(2) 判断String对象s1和s2是否相同: 成员运算符重载 全局运算符重载 class String { public: bool operator==(const String &) const; ... }; 调用方式:s1.operator==( s2 ) bool operator==(const String &, const String &); 调用方式:operator==( s1, s2 ) 东南大学计算机学院 11/21/2018

重载可交换的运算符(1) X 若两个操作对象是不同类型 成员运算符函数 左操作数不为本类对象 c = a + b c = b + a A a, c; B b; 调用方式: c = a.operator+(b) c = b.operator+(a) X 左操作数不为本类对象 函数原型: class A{ public: A operator+(const B &); }; class B{ public: A operator+(const A &); }; 东南大学计算机学院 11/21/2018

重载可交换的运算符(2) 若两个操作对象是不同类型 全局运算符函数 重载可交换的运算符只能用全局运算符函数 c = a + b c = b + a A a, c; B b; 调用方式: c = operator+(a, b) c = operator+(b, a) 函数原型: class A{ friend A operator+(const A &, const B &); friend A operator+(const B &, const A &); }; A operator+(const B & b, const A & a) { return operator+(a, b); } 重载可交换的运算符只能用全局运算符函数 东南大学计算机学院 11/21/2018

重载流插入/流提取运算符 东南大学计算机学院 11/21/2018

  重载流插入符 成员运算符函数 全局运算符函数 cout是内置类型ostream类的对象 cout << a class A (chap15) 成员运算符函数 全局运算符函数  调用方式: cout.operator<<(a) cout是内置类型ostream类的对象 这意味着需要修改ostream类,这是不可能的  调用方式: operator<<(cout, a) 函数原型: 返回值类型 operator<<(ostream &, const A &) 只能以全局运算符函数重载流插入(<<)/提取操作符(>>) 东南大学计算机学院 11/21/2018

例子(1) Phone number的输入输出 格式: (800) 555-1212 重载方式 必须为全局运算符函数 能够级联调用:cout<<a<<b 函数返回值类型:ostream & code: Fig. 11.3~11.5 string areaCode; string exchange; string line; 数据成员: areaCode exchange line (800) 555-1212 cout<<a; cout<<b; How? 回顾:返回this指针 东南大学计算机学院 11/21/2018

例子(2) 重载流插入符<< cout << phone operator<<(cout, phone) class PhoneNumber { friend ostream & operator<<(ostream &, const PhoneNumber & ); } 不需改变phone的值,传const引用 ostream & operator<<( ostream &output, const PhoneNumber &number ) { output << "(" << number.areaCode << ") " << number.exchange << "-" << number.line; return output; } 从重载的流操作符返回的cin/cout等大部分stream对象一般是全局的,返回引用一般会成功,不会虚悬引用 东南大学计算机学院 11/21/2018

例子(3) 重载流提取符>> cin >> phone operator>>(cin, phone) class PhoneNumber { friend istream & operator>>(istream &, PhoneNumber & ); } 需要改变phone的值,传引用 istream & operator>>( istream &input, PhoneNumber &number ) { input.ignore(); // skip ( input >> setw( 3 ) >> number.areaCode; // input area code input.ignore( 2 ); // skip ) and space input >> setw( 3 ) >> number.exchange; // input exchange input.ignore(); // skip dash (-) input >> setw( 4 ) >> number.line; // input line return input; // enables cin >> a >> b >> c; } setw()限定读到每个字符数组的个数

实例研究:Array类 基于指针的数组的缺点 Array类的功能 Array类实现 总结 东南大学计算机学院 11/21/2018

基于指针的数组的缺点 数组名是指向第一元素的const指针 缺点 下标必须是0,…,n-1 不能进行越界检查 不能对数组进行一次性输入/输出 只能针对单个元素分别进行 不能对两个数组进行有意义的比较运算(>, <, ==) 作为参数传递时,一般需要传递数组长度 不能使用赋值运算符(=)将一个数组赋值给另一个数组 int a[3]; a a[0] a[1] a[2] 数组名是const指针,不能用作左值 东南大学计算机学院 11/21/2018

扩展数组类 全局函数 Array类 重载<<和>>:实现数组整体的输入输出 重载赋值运算符=:将一个数组对象赋值给另一个数组 对象 重载关系运算符==:比较两个数组对象是否相同 重载关系运算符!=:比较两个数组对象是否不同 重载下标运算符[ ]: 可以进行越界检查 东南大学计算机学院 11/21/2018

Array类数据成员 class Array { …… private: int size; //数组长度 int *ptr; //数组名,指向第一个元素的地址 }; f(ptr, size) Array a; f(a); 东南大学计算机学院 11/21/2018

1.重载流插入/流提取符 class Array{ friend ostream & operator<<( ostream &, const Array & ); …… }; 必须用全局函数实现 不需改变a的值,传const引用 ostream &operator<<( ostream &output, const Array &a ){ int i; for ( i = 0; i < a.size; i++ ) { output << setw( 12 ) << a.ptr[ i ]; if ( ( i + 1 ) % 4 == 0 ) //每行4个元素 output << endl; } if ( i % 4 != 0 ) //最后一行回车 return output; // 级联调用,cout<<a<<b 1 2 3 4 5 6 7 东南大学计算机学院 11/21/2018

1.重载流插入/流提取符 class Array { friend istream & operator>>( istream &, Array & ); …… }; 必须用全局函数实现 需要改变a的值,传引用 istream & operator>>( istream & input, Array & a ) { for ( int i = 0; i < a.size; i++ ) input >> a.ptr[ i ]; return input; // 级联调用, cin>>a>>b } 东南大学计算机学院 11/21/2018

2. 默认构造函数 构造特定长度的数组,默认值为10 Tips: 动态数组的管理 参数有效性判断 初始化:否则数组元素为随机数! 函数声明: Array( int = 10 ); 18 Array::Array( int arraySize ) 19 { 20 size = ( arraySize > 0 ? arraySize : 10 ); // 参数有效性 21 ptr = new int[ size ]; // 动态数组,运行时确定长度 22 23 for ( int i = 0; i < size; i++ ) 24 ptr[ i ] = 0; // 数组初始化 } 东南大学计算机学院 11/21/2018

3. 拷贝构造函数(1) 将一个数组中的函数拷贝到当前数组 Tips: 动态数组管理 初始化 函数声明: Array( const Array & ); 参数为const引用,允许传入const实参 29 Array::Array( const Array &arrayToCopy ) 30 : size( arrayToCopy.size ) 31 { 32 ptr = new int[ size ]; // 动态数组,运行时确定长度 33 34 for ( int i = 0; i < size; i++ ) 35 ptr[ i ] = arrayToCopy.ptr[ i ]; //拷贝数组元素 } 东南大学计算机学院 11/21/2018

3. 拷贝构造函数(2) 拷贝构造函数的作用? 与默认的逐个成员赋值的区别 何时调用拷贝构造函数? 通过建立一个现有对象的副本来初始化一个Array对象 默认的赋值运算符”=“不自动调用拷贝构造函数,执行默认的逐个成员赋值 默认的逐个成员赋值会使两个指针指向同一内存区域,易引 起虚悬指针! a.ptr b.ptr 需要创建对象副本时 按值传递参数时 (函数形参/函数返回值) 用对象的副本初始化另一对象时 Array a; Array b = a; //调用拷贝构造函数,而非赋值运算符函数 东南大学计算机学院 11/21/2018

东南大学计算机学院 11/21/2018

Array::Array(Array arrayToCopy ) 3. 拷贝构造函数(3) 拷贝构造函数的参数类型为什么是const Array & ?可以 是Array 吗? const引用不需要拷贝const对象 不可以是Array,若为Array: Array::Array(Array arrayToCopy ) 需要创建实参的副本(拷贝),此时需要调用拷贝构造函数,即 该函数本身,造成无穷递归! 东南大学计算机学院 11/21/2018

4. 析构函数 Tips: 动态数组的内存释放 39 Array::~Array() 40 { 41 delete [] ptr; 42 } 40 { 41 delete [] ptr; 42 } 东南大学计算机学院 11/21/2018

5. 重载运算符“=” Tips: 重载方式:必须为成员运算符函数 利用拷贝构造函数 使用: 函数声明: Array a; Array b = a; b.operator=( a ); 函数声明: const Array &operator=( const Array & ) 右操作数a不改变,参数为const引用 左操作数b改变,不能为const函数 1. 为什么返回类型是const引用? 返回值类型指示赋值表达式的值的类型 const常量只能做右值,不能做左值 a = b = c是可以的,先执行b = c,再执行a = (b = c) 避免(a=b)=c. 东南大学计算机学院 11/21/2018

52 const Array &Array::operator=( const Array &right ) 53 { 53 { 54 if ( &right != this ) //避免自我赋值 55 { 56 //长度相等则直接拷贝 57 //不等则先释放原内存,再按长度申请新内存 58 if ( size != right.size ) 59 { 60 delete [] ptr; // release space 61 size = right.size; // resize this object 62 ptr = new int[ size ]; // create space for array copy 63 } // end inner if 64 65 for ( int i = 0; i < size; i++ ) 66 ptr[ i ] = right.ptr[ i ]; //拷贝 67 } // end outer if 68 69 return *this; //级联调用 a = b = c 70 } 2. 为什么要检测自我赋值? 若为自我赋值,则在赋值前,该对象的内存就被释放,导致致命的运行时错误 11/21/2018 东南大学计算机学院

6. 重载运算符“==”/“!=” Tips: 重载方式:成员运算符函数 利用已有函数 使用: 函数声明: Array a(10), b(11); bool f = (a == b); bool g = (a!=b); f = a.operator==( b ); g = a.operator!=( b ); 函数声明: bool operator==( const Array & ) const bool operator!=( const Array & ) const 左/右操作数都不改变,参数为const引用,函数为const函数 11/21/2018 东南大学计算机学院

6. 重载运算符“==”/“!=” 24 bool operator!=( const Array &right ) const 25 { 74 bool Array::operator==( const Array &right ) const 75 { 76 if ( size != right.size ) //首先判断长度是否相等 77 return false; //若不等,则返回false 78 79 for ( int i = 0; i < size; i++ ) //长度相等才判断数组元素是否都相同 80 if ( ptr[ i ] != right.ptr[ i ] ) 81 return false; // 只要有一个元素不同,就返回false 82 // 若遍历了整个数组都没有不同元素,就什么都不返回 83 return true; // 表明所有元素都相同,需要返回true 84 } 24 bool operator!=( const Array &right ) const 25 { 26 return ! ( *this == right ); //利用已有函数 27 } 11/21/2018 东南大学计算机学院

7. 重载运算符“[]”(1) Tips: 重载方式:成员运算符函数 需要进行越界检查 使用: 函数声明: 返回左值,能被赋值 Array a(3); int b = a[2]; a[2] = 1; //ok b = a.operator[]( 2 ); a a[0] a[1] a[2] 需返回的地址 需要返回特定下标的数组元素地址 函数声明: int & operator[]( int ); 返回左值,能被赋值 11/21/2018 东南大学计算机学院

7. 重载运算符“[]”(2) 88 int &Array::operator[]( int subscript ) 89 { 89 { 90 // 进行越界检查 91 if ( subscript < 0 || subscript >= size ) 92 { 93 cerr << "\nError: Subscript " << subscript 94 << " out of range" << endl; 95 exit( 1 ); //越界则退出程序 96 } 97 98 return ptr[ subscript ]; // 返回元素地址 99 } #include <cstdlib> using std::exit; 11/21/2018 东南大学计算机学院

7. 重载运算符“[]”(3) Tips: 要const版本的重载函数,以便const对象调用 (const对象只能调用const函数) 使用: const Array a(3); int b = a[2]; c [2] = 1; //error b = a.operator[]( 2 ); const版本函数 函数声明: int & operator[]( int ); 一般对象调用 int operator[]( int ) const; const对象调用 返回右值,不能被赋值 11/21/2018 东南大学计算机学院

7. 重载运算符“[]”(4) 103 int Array::operator[]( int subscript ) const 104 { 104 { 105 // 进行越界检查 106 if ( subscript < 0 || subscript >= size ) 107 { 108 cerr << "\nError: Subscript " << subscript 109 << " out of range" << endl; 110 exit( 1 ); 111 } 112 113 return ptr[ subscript ]; // 返回元素值 114 } 11/21/2018 东南大学计算机学院

总结(1) 关于动态内存管理 通常会为需要动态内存管理的类同时提供 若无拷贝构造函数和重载的赋值运算符 若无析构函数 拷贝构造函数(需要分配内存) 析构函数(需要释放内存) 重载的赋值运算符(需要删除原有内存并重新分配内存) 若无拷贝构造函数和重载的赋值运算符 执行操作:默认的逐个成员赋值 指向动态内存的指针指向同一地址,并造成内存泄漏 若无析构函数 不能自动释放动态内存区的变量 a.ptr b.ptr 东南大学计算机学院 11/21/2018

总结(2) 成员函数为private 重载的赋值运算符函数为private:阻止对象赋值 拷贝构造函数为private:阻止对象拷贝 无法调用默认的赋值运算函数,它被重载 无法调用默认的拷贝构造函数,它被重载 东南大学计算机学院 11/21/2018