C++ Programming Lecture 16 Wei Liu (刘威) Dept. of Electronics and Information Eng. Huazhong University of Science and Technology Feb. 2014 Lecture 16 Chapter 19. Operator Overloading 19.1 Introduction 19.2 Fundamentals of Operator Overloading 19.3 Restrictions on Operator Overloading 19.4 Operator Functions as Class Members vs. Global Functions 19.5 Overloading Stream Insertion and Stream Extraction Operators 19.6 Overloading Unary Operators 19.7 Overloading Binary Operators 19.8 Dynamic Memory Management 19.9 Case Study: Array Class 19.10 Converting between Types 19.11 Case Study: String Class 19.12 Overloading ++ and -19.13 Case Study: A Date Class 19.14 Standard Library Class string 19.15 explicit Constructors -2- Chapter 19. Operator Overloading 运算符重载 对特定的类定义相应的运算符的功能 通过函数来实现 学习要点 实现对象运算的成员函数如何设计? 运算符重载的成员函数如何定义?如何调用? 运算符重载的全局函数如何定义?如何调用? -3- 案例讨论:时间类 operator1.cpp … show函数输出“xx:xx”的字符信息 strstream是C++标准string流处理类,该类可以接 受流式输入,也可以转换为特定类型的对象输出 -4- 案例讨论:时间类 operator1.cpp 形参是对象的引用,降低传递的开销 函数sum是const表示不会修改对象this 设计sum函数,完成两个time对象相加的功能: result = this + more 对象more是函数sum的一个参数 对象result是由函数sum创建的临时对象 -5- 案例讨论:时间类 operator1.cpp -6- 案例讨论:时间类 operator1.cpp 该sum函数返回值只能设为 Time对象,而不能是对象的引用。Why? operator2.cpp -7- 案例讨论:时间类 operator1.cpp 该sum函数,可以改造成等效的运算符重载函数operator+ operator3.cpp -8- 案例讨论:时间类 operator3.cpp operator+可以显式的以函数调 用的形式调用: a.operator+(b) operator+也可以用表达式的形 式调用: a+b operator+的函数调用方式参考 运算符+的结合性来展开 受限于函数调用关系,对象a的成员函数operator+只能支持 a + b 形式的函数调用,不能支持 b + a 形式的函数调用 如果希望表达式的第一个操作数可以不是对象,则需要采用 全局函数的方法 -9- 案例讨论:时间类 operator4.cpp Time * int_k 的表达式 用Time类的成员函数重载 operator* 来实现,该函数 的参数是int_k int_k * Time 的表达式 用Time类的全局友元函数重载 operator* 来实现,该函数的参 数分别是int_k , Time_t -10- 案例讨论:时间类 operator4.cpp Time成员函数重载运算符,支 持Time * int_k 的表达式 Time的全局友元函数重载运算 符,支持int_k * Time 的表达 式 -11- 小结 运算符重载的学习要点 实现对象运算的成员函数如何设计? 返回值可以是一个对象。函数调用完毕后,该对象将以函数返回值的形 式复制给主调函数的接受对象。 返回值可以是一个引用。但是该引用必须能在函数调用完毕后有意义, 即不能使“悬挂引用” 运算符重载的成员函数如何定义?如何调用? 二元运算符重载时,该函数只需要接收另一个对象more作为参数 调用该函数时,this对象出现在运算符左边,more对象出现在右边 运算符重载的全局函数如何定义?如何调用? 二元运算符重载时,该函数需要接收两个对象作为参数 调用该函数时,第一个实参在运算符左边,第二个实参在右边 -12- Experiment 改写Time类,重载运算符,支持时间的相减运算 1. 采用成员函数 2. 采用全局友元函数 3. 采用全局非友元函数 Lecture 16 Chapter 19. Operator Overloading 19.1 Introduction 19.2 Fundamentals of Operator Overloading 19.3 Restrictions on Operator Overloading 19.4 Operator Functions as Class Members vs. Global Functions 19.5 Overloading Stream Insertion and Stream Extraction Operators 19.6 Overloading Unary Operators 19.7 Overloading Binary Operators 19.8 Dynamic Memory Management 19.9 Case Study: Array Class 19.10 Converting between Types 19.11 Case Study: String Class 19.12 Overloading ++ and -19.13 Case Study: A Date Class 19.14 Standard Library Class string 19.15 explicit Constructors -14- 19.2 Fundamentals Three exceptions in operator overloading 要对类的对象使用运算符, 就需要运算符重载,三种例外情况是: The assignment operator (=) may be used with every class to perform memberwise assignment of the class’s data members. 赋值运算符无需重载 The address (&) and comma (,) operators may also be used with objects of any class without overloading. 取地址和逗号运算符无需重载 -15- 19.3 Restrictions Restrictions The precedence of an operator cannot be changed 重载不能 改变运算符的优先级 The associativity of an operator (i.e., whether the operator is applied right-to-left or left-to-right) cannot be changed 重 载不能改变运算符的结合律 It isn’t possible to change the “arity” of an operator 重载不 能改变运算符的元数 相关的运算符不会自动重载 = vs. += == vs. != -16- 19.4 Operator Functions as Class Members vs. Global Functions Class Members成员函数 Global Functions全局函数 参数调用 用this指针隐式的获得一个类的 对象的实参,即二元运算符的第 一个操作数 用显式的方式获得二元运算符的 两个操作数 函数调用 最左侧的操作数必须是该运算符 所属类的对象 如果左操作数必须是其它类的对 象或者基本类型对象(例如int, double),则运算符函数必须实 现为全局函数 如果必须直接访问private或者 protected成员,则该函数必须 是该类的友元函数 重载限制 必须重载为类的成员的运算符: ( ) [ ] -> = += -= 下列运算符通常重载为全局函数: << >> -17- Lecture 16 Chapter 19. Operator Overloading 19.1 Introduction 19.2 Fundamentals of Operator Overloading 19.3 Restrictions on Operator Overloading 19.4 Operator Functions as Class Members vs. Global Functions 19.5 Overloading Stream Insertion and Stream Extraction Operators 19.6 Overloading Unary Operators 19.7 Overloading Binary Operators 19.8 Dynamic Memory Management 19.9 Case Study: Array Class 19.10 Converting between Types 19.11 Case Study: String Class 19.12 Overloading ++ and -19.13 Case Study: A Date Class 19.14 Standard Library Class string 19.15 explicit Constructors -18- 11.8 Dynamic Memory Management dynamic memory management 动态内存分配 C++ enables you to control the allocation and deallocation. performed with operator new and delete. new operator 获得动态内存 to dynamically allocate (i.e., reserve) the exact amount of memory required to hold an object or array at execution time. The object or array is created in the free store (also called the heap)—a region of memory assigned to each program for storing dynamically allocated objects. delete operator 释放动态内存 You can return memory to the free store by using the delete operator to deallocate it. -19- 案例讨论:时间类 operator6.cpp 在Time构造和析构函数中增加 打印信息 可以观察到表达式运算过 程中临时对象的创建和销 毁 -20- 案例讨论:时间类 operator7.cpp 采用new[ ]动态创建Time数组 时,只能采用默认构造函数 可以观察到表达式运算过采用 new[ ]动态创建的对象数组,需 要用delete[ ] 释放相应的内存 -21- 11.9 Case Study: Array Class In this example, we create a powerful Array class: Performs range checking. 支持下标边界检查 Allows one array object to be as-signed to another with the assignment operator. 支持数组直接赋值 Objects know their own size. 数组知道自己的大小 Input or output entire arrays with the stream extraction and stream insertion operators, respectively. 数组可以整体输 入和输出 Can compare Arrays with the equality operators == and !=. 数组可以整体进行比较 C++ Standard Library class template vector provides many of these capabilities as well. -22- uses new to obtain the memory for the internal pointer-based representation of this array and assigns the pointer returned by new to data member ptr uses delete [] to release the memory allocated dynamically by new in the constructor. When the compiler sees the expression integers1 = integers2, the compiler invokes member function operator= with the call integers1.operator=(integer s2) returning the current object (i.e., *this as a constant reference; this enables cascaded Array assignments such as x=y=z When the compiler sees the expression integers1 == integers2, the compiler invokes member function operator== with the call integers1.operator==(integers2 ) When the compiler sees the expression integers1[5] it invokes the appropriate overloaded operator[] member function by gener-ating the call integers1.operator[]( 5 ) If the subscript is in range, the non-const version of operator[] returns the appropriate array element as a reference so that it may be used as a modifiable lvalue. If the subscript is in range, the const version of operator[] returns a copy of the appropriate element of the array. These stream insertion and stream extraction operator functions cannot be members of class Array, because the Array object is always mentioned on the right side of the stream insertion operator and the stream extraction operator. Lecture 16 Chapter 19. Operator Overloading 19.1 Introduction 19.2 Fundamentals of Operator Overloading 19.3 Restrictions on Operator Overloading 19.4 Operator Functions as Class Members vs. Global Functions 19.5 Overloading Stream Insertion and Stream Extraction Operators 19.6 Overloading Unary Operators 19.7 Overloading Binary Operators 19.8 Dynamic Memory Management 19.9 Case Study: Array Class 19.10 Converting between Types 19.11 Case Study: String Class 19.12 Overloading ++ and -19.13 Case Study: A Date Class 19.14 Standard Library Class string 19.15 explicit Constructors -30- 19.14 Standard Library Class string -31- -32- -33- -34- -35- Lecture 16 Chapter 20. Object-Oriented Programming: Inheritance 20.1 Introduction 20.2 Base Classes and Derived Classes 20.3 protected Members 20.4 Relationship between Base Classes and Derived Classes 20.5 Constructors and Destructors in Derived Classes 20.6 public, protected and private Inheritance 20.7 Software Engineering with Inheritance -36- 20.1 Introduction Inheritance is a form of software reuse in which you create a class that absorbs an existing class’s data and behaviors and enhances them with new capabilities. You can designate that the new class should inherit the members of an existing class. 继承是一种软件重用的方式。如果现有类可以描述事物的 现有数据和行为,通过继承来获得和增强类的能力 This existing class is called the base class, and the new class is referred to as the derived class. 现有的类被称为基类,从基类继承出来的类被称为派生类 -37- Student + int id; + string name; + float score; - char gender; Student + Student() + display() Graduate Master 硕士研究生 学生 研究生 Ph.D 博士研究生 Graduate + int id; + string name; + float score; - char gender; - float pay; + Graduate() + display() -38- Class of student : student.h Protected 表示数据成员可以被继承 Priviate 表示数据成员在继承时将被隐藏, 不能直接访问 -39- Class of student : studend.cpp -40- Class of graduate: graduate.h 表示继承Student类的所有公共属性 派生类可以创建新的数据成员、新的成员函数、改写基类的成员函数 基类的数据成员和成员函数都自动地可以被派生类访问 -41- Class of graduate: graduate.cpp 派生类访问基类的成员函数需要用域说明符:: -42- Using inheritance -43- Advanced programming (1) Default arguments in constructor Calling parent constructor in child class 派生类不必使用基类的全部数据成员 -44- Advanced programming (2) Visiting protected / private data members of parent class 派生类不能访问基类的私有数据成员 派生类可以通过访问基类的公共成员函数来间接获取私有数据成员的值 -45- 作业 19.9 实现超大整数HugeInt类的运算符重载 设计一个基类Event和两个派生类Meeting,Lecture, 编写测试程序验证其功能 Event类描述时间、地点、事件; Meeting类描述时间、地点、会议议程、参会人员; Lecture类描述时间、教室地点、授课教师、课程名称、授 课内容 预习多态,结合上面的题目,编写基类和派生类的 display()函数,展示多态性 -46- 谢谢! 刘威 副教授 互联网技术与工程研究中心 华中科技大学电子与信息工程系 电话:13986224922 Email: liuwei@hust.edu.cn 网址:http://itec.hust.edu.cn