北京大学

Similar documents
Microsoft PowerPoint - 8. 运算符重载 Operator Overloading.pptx

运算符重载 为什么要 运算符重载 那些运算符可以重载, 哪些不可以 如何实现运算符重载 实现方式 : 成员函数与非成员函数 类型转换 怎样实现对象与基本数据类型数据的运算 2

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1

Microsoft PowerPoint - string_kruse [兼容模式]

北京大学

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

IO

C/C++语言 - C/C++数据

新版 明解C++入門編

幻灯片 1

C++ 程序设计 实验 2 - 参考答案 MASTER 2017 年 5 月 21 日 1

C++ 程序设计 告别 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

2013 C 1 # include <stdio.h> 2 int main ( void ) 3 { 4 int cases, a, b, i; 5 scanf ("%d", & cases ); 6 for (i = 0;i < cases ;i ++) 7 { 8 scanf ("%d %d

Microsoft PowerPoint - 5. 指针Pointers.ppt [兼容模式]

Microsoft PowerPoint - 4. 数组和字符串Arrays and Strings.ppt [兼容模式]

《计算概论》课程 第十九讲 C 程序设计语言应用

c_cpp

エスポラージュ株式会社 住所 : 東京都江東区大島 東急ドエルアルス大島 HP: ******************* * 关于 Java 测试试题 ******

C++ 程序设计 实验 3 - 参考答案 MASTER 2017 年 5 月 21 日 1

C 1 # include <stdio.h> 2 int main ( void ) { 4 int cases, i; 5 long long a, b; 6 scanf ("%d", & cases ); 7 for (i = 0;i < cases ;i ++) 8 { 9

OOP with Java 通知 Project 4: 5 月 2 日晚 9 点

OOP with Java 通知 Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢 学习使用文本编辑器 学习使用 cmd: Power shell 阅读参考资料

chap07.key

Microsoft PowerPoint - 第06讲_继承.ppt [兼容模式]

NOWOER.OM m/n m/=n m/n m%=n m%n m%=n m%n m/=n 4. enum string x1, x2, x3=10, x4, x5, x; 函数外部问 x 等于什么? 随机值 5. unsigned char *p1; unsigned long *p

FY.DOC

C/C++程序设计 - 字符串与格式化输入/输出

Microsoft PowerPoint - 04-Inheritance.ppt

没有幻灯片标题

Microsoft PowerPoint - 07 派生数据类型

untitled

untitled

上海交通大学

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.06.doc

<4D F736F F F696E74202D20B5DA3032BDB25FC0E0BACDB6D4CFF3312E BBCE6C8DDC4A3CABD5D>

C++ 程序设计 OJ2 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 OJ9 - 参考答案 MASTER 2019 年 6 月 7 日 1

第3章.doc

02

Microsoft Word - 新1-12.doc

《C语言程序设计》第2版教材习题参考答案

Microsoft PowerPoint - 10 模板 Template.pptx

Generated by Unregistered Batch DOC TO PDF Converter , please register! 浙江大学 C 程序设计及实验 试题卷 学年春季学期考试时间 : 2003 年 6 月 20 日上午 8:3

试卷代号 ~1075 座位号 E 口 国家开放大学 ( 中央广播电视大学 )20]5 年秋季学期 " 开放本科 " 期末考试 C 十十语言程序设计 试题 同二二十斗 2016 年 1 月 巴叫一 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new D. l

试卷代号 :1075 座位号 rn 国家开放大学 ( 中央广播电视大学 )2015 年秋季学期 " 开放本科 " 期末考试 c+ 十语言程序设计试题 2016 年 1 月 t 问一 Urr-f 斗 士 1 1. 下面的保留字 ( ) 不能作为函数的返回类型 A. void B. int C. new

untitled

《C语言程序设计》教材习题参考答案

幻灯片 1

PowerPoint Presentation

Microsoft PowerPoint - 3. 函数Functionl.ppt [兼容模式]

4. if 与 else 在使用过程中为避免嵌套出现二义性,C++ 中规定与 else 子句配对的是 ( ) A. 其之前最近的 if 语句 B. 其之前最近且尚未配对的 if 语句 C. 缩排位置相同的 if 语句 D. 其之后最近的 if 语句答案 :B 解析 :C++ 中规定与 else 子句

Microsoft PowerPoint - 01_Introduction.ppt

C++ 程序设计 OJ4 - 参考答案 MASTER 2017 年 5 月 21 日 1

提问袁小兵:

新・解きながら学ぶJava

网C试题(08上).doc

OOP with Java 通知 : Project 2 提交时间 : 3 月 14 日晚 9 点 另一名助教 : 王桢

Microsoft PowerPoint - 6. 用户定义类型User-defined Datatypes.ppt [兼容模式]

OOP with Java 通知 : Project 2 提交时间 : 3 月 15 日晚 9 点

无类继承.key

C++ 程序设计 OJ4 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 OJ10 - 参考答案 MASTER 2019 年 6 月 17 日 1

Microsoft Word cppFinalSolution.doc

Microsoft Word - 新正文.doc

untitled

程序设计语言及基础

untitled

Guava学习之Resources

内 容 提 要 指 针 持 久 动 态 内 存 分 配 字 符 串 ( 字 符 数 组 ) 2

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

Strings

Microsoft PowerPoint - 1. C++介绍.ppt [兼容模式]

Microsoft Word - 第3章.doc

C/C++语言 - 运算符、表达式和语句

第七讲 继承与多态

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.07.doc

CHAPTER VC#

Chapter12 Derived Classes

untitled

C/C++ - 文件IO

PowerPoint 演示文稿

untitled

OOP with Java 通知 Project 4: 推迟至 4 月 25 日晚 9 点

C++ 程序设计 实验 1 - 参考答案 MASTER 2017 年 5 月 21 日 1

幻灯片 1

<4D F736F F F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

Microsoft PowerPoint - 06_Struct.ppt

C 1

新・解きながら学ぶC言語

C++ 程式設計

2015年计算机二级(C语言)模拟试题及答案(三)

新版 明解C言語入門編

幻灯片 1

程序设计与算法语言 静态与友元 C/C++ Programming and Algorithms Static, Friend and Const Dongke Sun ( 孙东科 ) 东南大学机械工程学院 School of Mechanical Engineer

C C

全国计算机技术与软件专业技术资格(水平)考试

新・明解C言語入門編『索引』

Transcription:

1 运算符重载 基本概念 郭炜刘家瑛 北京大学程序设计实习

运算符 C++ 预定义表示对数据的运算 +, -, *, /, %, ^, &, ~,!,, =, <<, >>,!= 只能用于基本的数据类型 整型, 实型, 字符型, 逻辑型 2

自定义数据类型与运算符重载 C++ 提供了数据抽象的手段 : 用户自己定义数据类型 -- 类 调用类的成员函数 操作它的对象 类的成员函数 操作对象时, 很不方便 在数学上, 两个复数可以直接进行 +/- 等运算 Vs. 在 C++ 中, 直接将 + 或 - 用于复数是不允许的 3

运算符重载 对抽象数据类型也能够直接使用 C++ 提供的运算符 程序更简洁 代码更容易理解 例如 : complex_a 和 complex_b 是两个复数对象 求两个复数的和, 希望能直接写 : complex_a + complex_b 4

运算符重载 运算符重载 对已有的运算符赋予多重的含义 使同一运算符作用于不同类型的数据时 不同类型的行为 目的 扩展 C++ 中提供的运算符的适用范围, 以用于类所表示的抽象数据类型 同一个运算符, 对不同类型的操作数, 所发生的行为不同 (5,10i) + (4,8i) = (9,18i) 5 + 4 = 9 5

运算符重载 运算符重载的实质是函数重载 返回值类型 operator 运算符 ( 形参表 ) { 6

运算符重载 在程序编译时 : 把含运算符的表达式 对运算符函数的调用 把运算符的操作数 运算符函数的参数 运算符被多次重载时, 根据实参的类型决定调用哪个运算符函数 运算符可以被重载成普通函数 也可以被重载成类的成员函数 7

运算符重载为普通函数 class Complex { public: Complex( double r = 0.0, double i= 0.0 ){ real = r; imaginary = i; double real; // real part double imaginary; // imaginary part ; 8

Complex operator+ (const Complex & a, const Complex & b) { return Complex( a.real+b.real, a.imaginary+b.imaginary); // 类名 ( 参数表 ) 就代表一个对象 Complex a(1,2), b(2,3), c; c = a + b; // 相当于什么? 重载为普通函数时, 参数个数为运算符目数 9

运算符重载为成员函数 class Complex { public: Complex( double r= 0.0, double m = 0.0 ): real(r), imaginary(m) { // constructor Complex operator+ ( const Complex & ); // addition Complex operator- ( const Complex & ); // subtraction private: double real; // real part double imaginary; // imaginary part ; 重载为成员函数时, 参数个数为运算符目数减一 10

// Overloaded addition operator Complex Complex::operator+(const Complex & operand2) { return Complex( real + operand2.real, imaginary + operand2.imaginary ); // Overloaded subtraction operator Complex Complex::operator- (const Complex & operand2){ return Complex( real - operand2.real, imaginary - operand2.imaginary ); int main(){ Complex x, y(4.3, 8.2), z(3.3, 1.1); x = y + z; x = y - z; return 0; // 相当于什么? // 相当于什么? 11

1 赋值运算符的重载 郭炜刘家瑛 北京大学 程序设计实习

赋值运算符 = 重载 赋值运算符两边的类型可以不匹配 把一个 int 类型变量赋值给一个 Complex 对象 把一个 char * 类型的字符串赋值给一个字符串对象 需要重载赋值运算符 = 赋值运算符 = 只能重载为成员函数 2

编写一个长度可变的字符串类 String 包含一个 char * 类型的成员变量 指向动态分配的存储空间 该存储空间用于存放 \0 结尾的字符串 class String { private: char * str; public: String () : str(null) { // 构造函数, 初始化 str 为 NULL const char * c_str() { return str; char * operator = (const char * s); ~String( ); ; 3

// 重载 = obj = hello 能够成立 char * String::operator = (const char * s){ if(str) delete [] str; if(s) { //s 不为 NULL 才会执行拷贝 str = new char[strlen(s)+1]; strcpy(str, s); else str = NULL; return str; 4

String::~String( ) { ; if(str) delete [] str; int main(){ String s; s = Good Luck, ; cout << s.c_str() << endl; // String s2 = hello! ; // 这条语句要是不注释掉就会出错 s = "Shenzhou 8!"; cout << s.c_str() << endl; return 0; 程序输出结果 : Good Luck, Shenzhou 8!

重载赋值运算符的意义 浅复制和深复制 S1 = S2; 浅复制 / 浅拷贝 执行逐个字节的复制工作 MyString S1, S2; S1 = this ; S2 = that ; S1 = S2; 6

s1 str s2 str t h i s \0 t h a t \0 String S1, S2; S1 = this ; S2 = that ; s1 str s2 str t h i s \0 t h a t \0 S1 = S2; 7

重载赋值运算符的意义 浅复制和深复制 深复制 / 深拷贝 将一个对象中指针变量指向的内容, 复制到另一个对象中指针成员对象指向的地方 MyString S1, S2; S1 = this ; S2 = that ; S1 = S2; 8

s1 str s2 str t h i s \0 t h a t \0 String S1, S2; S1 = this ; S2 = that ; s1 str s2 str t h a t \0 t h a t \0 S1 = S2; 9

MyString S1, S2; S1 = this ; S2 = that ; S1 = S2; 在 class MyString 里添加成员函数 : String & operator = (const String & s) { if(str) delete [] str; str = new char[strlen(s.str)+1]; strcpy(str, s.str); return * this; 10

思考 考虑下面语句 : MyString s; s = Hello ; s = s; 是否会有问题? 11

思考 正确写法 : String & String::operator = (const String & s){ if(str == s.str) return * this; if(str) delete [] str; if(s.str) { //s.str 不为 NULL 才会执行拷贝 str = new char[strlen(s.str)+1]; strcpy( str,s.str); else str = NULL; return * this; 12

对 operator = 返回值类型的讨论 void 好不好? 考虑 : a = b = c; // 等价于 a.operator=(b.operator=(c)); String 好不好? 为什么是 String & 运算符重载时, 好的风格 -- 尽量保留运算符原本的特性考虑 : (a=b)=c; // 会修改 a 的值分别等价于 : (a.operator=(b)).operator=(c); 13

上面的 String 类是否就没有问题了? 为 String 类编写复制构造函数时会面临和 = 同样的问题, 用同样的方法处理 String::String(String & s) { if(s.str) { str = new char[strlen(s.str)+1]; strcpy(str, s.str); else str = NULL; 14

1 运算符重载为友元函数 郭炜刘家瑛 北京大学程序设计实习

运算符重载为友元 通常, 将运算符重载为类的成员函数重载为友元函数的情况 : 成员函数不能满足使用要求 普通函数, 又不能访问类的私有成员 2

运算符重载为友元 class Complex{ double real, imag; public: Complex(double r, double i):real(r), imag(i){ ; Complex operator+(double r); ; Complex Complex::operator+(double r){ // 能解释 c+5 return Complex(real + r, imag); 3

经过上述重载后 : Complex c ; c = c + 5; // 有定义, 相当于 c = c.operator +(5); 但是 : c = 5 + c; // 编译出错 为了使得上述表达式能成立, 需要将 + 重载为普通函数 Complex operator+ (double r, const Complex & c) { // 能解释 5+c return Complex( c.real + r, c.imag); 4

普通函数不能访问私有成员 将运算符 + 重载为友元函数 class Complex { double real, imag; public: Complex( double r, double i):real(r),imag(i){ ; Complex operator+( double r ); friend Complex operator + (double r, const Complex & c); ; 5

信息科学技术学院 程序设计实习 郭炜微博 http://weibo.com/guoweiofpku http://blog.sina.com.cn/u/3266490431 刘家瑛微博 http://weibo.com/pkuliujiaying 1

信息科学技术学院 程序设计实习 郭炜刘家瑛 运算符重载实例 : 可变长整型数组 ( 教材 P215) 2

int main() { // 要编写可变长整型数组类, 使之能如下使用 : CArray a; // 开始里的数组是空的 for( int i = 0;i < 5;++i) a.push_back(i); CArray a2,a3; a2 = a; 要重载 = for( int i = 0; i < a.length(); ++i ) cout << a2[i] << " " ; 要重载 [ ] a2 = a3; //a2 是空的 for( int i = 0; i < a2.length(); ++i ) //a2.length() 返回 0 cout << a2[i] << " "; cout << endl; a[3] = 100; CArray a4(a); for( int i = 0; i < a4.length(); ++i ) cout << a4[i] << " "; return 0; 要用动态分配的内存来存放数组元素, 需要一个指针成员变量 要自己写复制构造函数 程序输出结果是 : 0 1 2 3 4 0 1 2 100 4 要做哪些事情? 3

class CArray { int size; // 数组元素的个数 int *ptr; // 指向动态分配的数组 public: CArray(int s = 0); //s 代表数组元素的个数 CArray(CArray & a); ~CArray(); void push_back(int v); // 用于在数组尾部添加一个元素 v CArray & operator=( const CArray & a); // 用于数组对象间的赋值 int length() { return size; // 返回数组元素个数 CArray::operator[](int i) // 返回值是什么类型? {// 用以支持根据下标访问数组元素, // 如 n = a[i] 和 a[i] = 4; 这样的语句 return ptr[i]; ; 4

class CArray { int size; // 数组元素的个数 int *ptr; // 指向动态分配的数组 public: CArray(int s = 0); //s 代表数组元素的个数 CArray(CArray & a); ~CArray(); void push_back(int v); // 用于在数组尾部添加一个元素 v CArray & operator=( const CArray & a); // 用于数组对象间的赋值 int length() { return size; // 返回数组元素个数 int & CArray::operator[](int i) // 返回值为 int 不行! 不支持 a[i] = 4 {// 用以支持根据下标访问数组元素, // 如 n = a[i] 和 a[i] = 4; 这样的语句 return ptr[i]; ; 5

CArray::CArray(int s):size(s) { if( s == 0) ptr = NULL; else ptr = new int[s]; CArray::CArray(CArray & a) { if(!a.ptr) { ptr = NULL; size = 0; return; ptr = new int[a.size]; memcpy( ptr, a.ptr, sizeof(int ) * a.size); size = a.size; 6

a1 ptr 11 14 9 8 CArray a2(a1); a2 ptr a1 ptr 11 14 9 8 a2 ptr a1 11 14 9 8 ptr 11 14 9 8

CArray::~CArray() { if( ptr) delete [] ptr; CArray & CArray::operator=( const CArray & a) { // 赋值号的作用是使 = 左边对象里存放的数组, 大小和内容都和右边的对象一样 if( ptr == a.ptr) // 防止 a=a 这样的赋值导致出错 return * this; if( a.ptr == NULL) { // 如果 a 里面的数组是空的 if( ptr ) delete [] ptr; ptr = NULL; size = 0; return * this; 8

if( size < a.size) { // 如果原有空间够大, 就不用分配新的空间 if(ptr) delete [] ptr; ptr = new int[a.size]; memcpy( ptr,a.ptr,sizeof(int)*a.size); size = a.size; return * this; // CArray & CArray::operator=( const CArray & a) 9

void CArray::push_back(int v) { // 在数组尾部添加一个元素 if( ptr) { int * tmpptr = new int[size+1]; // 重新分配空间 memcpy(tmpptr,ptr,sizeof(int)*size); // 拷贝原数组内容 delete [] ptr; ptr = tmpptr; else // 数组本来是空的 ptr = new int[1]; ptr[size++] = v; // 加入新的数组元素 10

信息科学技术学院 程序设计实习 郭炜微博 http://weibo.com/guoweiofpku http://blog.sina.com.cn/u/3266490431 刘家瑛微博 http://weibo.com/pkuliujiaying

信息科学技术学院 程序设计实习 郭炜刘家瑛 流插入运算符和流提取运算符的重载 ( 教材 P218)

问题 cout << 5 << this ; 为什么能够成立? cout 是什么? << 为什么能用在 cout 上?

流插入运算符的重载 cout 是在 iostream 中定义的,ostream 类的对象 << 能用在 cout 上是因为, 在 iostream 里对 << 进行了重载 考虑, 怎么重载才能使得 cout << 5; 和 cout << this 都能成立?

流插入运算符的重载 有可能按以下方式重载成 ostream 类的成员函数 : void ostream::operator<<(int n) { // 输出 n 的代码 return;

流插入运算符的重载 cout << 5 ; 即 cout.operator<<(5); cout << this ; 即 cout.operator<<( this ); 怎么重载才能使得 cout << 5 << this ; 成立?

流插入运算符的重载 ostream & ostream::operator<<(int n) { // 输出 n 的代码 return * this; ostream & ostream::operator<<( const char * s ) { // 输出 s 的代码 return * this;

流插入运算符的重载 cout << 5 << this ; 本质上的函数调用的形式是什么? cout.operator<<(5).operator<<( this );

流插入运算符的重载 假定下面程序输出为 5hello, 该补写些什么 class CStudent{ public: int nage; ; int main(){ CStudent s ; s.nage = 5; cout << s <<"hello"; return 0;

流插入运算符的重载 ostream & operator<<( ostream & o,const CStudent & s){ o << s.nage ; return o;

例题 ( 教材 P218) 假定 c 是 Complex 复数类的对象, 现在希望写 cout << c;, 就能以 a+bi 的形式输出 c 的值, 写 cin>>c;, 就能从键盘接受 a+bi 形式的输入, 并且使得 c.real = a,c.imag = b

例题 int main() { Complex c; int n; cin >> c >> n; cout << c << "," << n; return 0; 程序运行结果可以如下 : 13.2+133i 87 13.2+133i, 87

#include <iostream> #include <string> #include <cstdlib> using namespace std; class Complex { double real,imag; public: Complex( double r=0, double i=0):real(r),imag(i){ ; friend ostream & operator<<( ostream & os, const Complex & c); friend istream & operator>>( istream & is,complex & c); ; ostream & operator<<( ostream & os,const Complex & c) { os << c.real << "+" << c.imag << "i"; // 以 "a+bi" 的形式输出 return os;

istream & operator>>( istream & is,complex & c) { string s; is >> s; // 将 "a+bi" 作为字符串读入, a+bi 中间不能有空格 int pos = s.find("+",0); string stmp = s.substr(0,pos); // 分离出代表实部的字符串 c.real = atof(stmp.c_str());//atof 库函数能将 const char* 指针指向的内容转换成 float stmp = s.substr(pos+1, s.length()-pos-2); // 分离出代表虚部的字符串 c.imag = atof(stmp.c_str()); return is;

int main() { Complex c; int n; cin >> c >> n; cout << c << "," << n; return 0; 程序运行结果可以如下 : 13.2+133i 87 13.2+133i, 87

1 自加 / 自减运算符的重载 郭炜刘家瑛 北京大学 程序设计实习

自加 / 自减运算符的重载 自加 ++/ 自减 -- 运算符有前置 / 后置之分前置运算符作为一元运算符重载 重载为成员函数 : T & operator++(); T & operator--(); 重载为全局函数 : T & operator++(t &); T & operator (T &); ++obj, obj.operator++(), operator++(obj) 都调用上述函数 2

自加 / 自减运算符的重载 后置运算符作为二元运算符重载 多写一个参数, 具体无意义 重载为成员函数 : T operator++(int); T operator--(int); 重载为全局函数 : T operator++(t &, int); T operator--(t &, int); obj++, obj.operator++(0), operator++(obj,0) 都调用上函数 3

int main(){ CDemo d(5); cout << (d++) << ","; // 等价于 d.operator++(0); cout << d << ","; cout << (++d) << ","; // 等价于 d.operator++(); cout << d << endl; cout << (d--) << ","; // 等价于 operator--(d,0); cout << d << ","; cout << (--d) << ","; // 等价于 operator--(d); cout << d << endl; return 0; 程序输出结果 : 5,6,7,7 7,6,5,5 如何编写 CDemo? 4

class CDemo { private : int n; public: CDemo(int i=0):n(i) { CDemo & operator++(); // 用于前置 ++ 形式 CDemo operator++(int); // 用于后置 ++ 形式 operator int ( ) { return n; friend CDemo & operator--(cdemo &); // 用于前置 -- 形式 friend CDemo operator--(cdemo &, int); // 用于后置 -- 形式 ; CDemo & CDemo::operator++() { // 前置 ++ n++; return * this; 5

CDemo CDemo::operator++(int k) { // 后置 ++ CDemo tmp(*this); // 记录修改前的对象 n++; return tmp; // 返回修改前的对象 CDemo & operator--(cdemo & d) { // 前置 -- d.n--; return d; CDemo operator--(cdemo & d, int) { // 后置 -- CDemo tmp(d); d.n --; return tmp; 6

operator int ( ) { return n; int 作为一个类型强制转换运算符被重载, Demo s; (int) s ; // 等效于 s.int(); 类型强制转换运算符重载时, 不能写返回值类型 实际上其返回值类型 -- 类型强制转换运算符代表的类型 7

运算符重载的注意事项 C++ 不允许定义新的运算符 重载后运算符的含义应该符合日常习惯 complex_a + complex_b word_a > word_b date_b = date_a + n 运算符重载不改变运算符的优先级 以下运算符不能被重载 :.,.*, ::,?:, sizeof 重载运算符 (), [ ], -> 或者赋值运算符 = 时, 重载函数必须声明为类的成员函数 8