Presentation is loading. Please wait.

Presentation is loading. Please wait.

Operator Overloading; String and Array Objects

Similar presentations


Presentation on theme: "Operator Overloading; String and Array Objects"— Presentation transcript:

1 Operator Overloading; String and Array Objects
Chapter 11 Operator Overloading; String and Array Objects

2 OBJECTIVES What operator overloading is and how it makes programs more readable and programming more convenient. To redefine (overload) operators to work with objects of user-defined classes. The differences between overloading unary and binary operators. To convert objects from one class to another class. When to, and when not to, overload operators.

3 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

4 11.1 Introduction 复习:函数重载 要求 函数名相同,参数列表不同 仅返回值不同不作为重载 注意:有缺省参数的函数
2018年11月30日5时32分 11.1 Introduction 复习:函数重载 要求 函数名相同,参数列表不同 仅返回值不同不作为重载 注意:有缺省参数的函数 func(int i, int j = 0) func(int i) 特例:const成员函数 void Employee::display(){} void Employee::display() const {} pNum+1什么效果?指针值+1?

5 11.1 Introduction cout << int_variable;// 整型变量
2018年11月30日5时32分 11.1 Introduction cout << int_variable;// 整型变量 cout << ptrInt;// 整型指针 cout << ptrChar;// 字符指针 “<< ”流插入运算符 & 按位左移运算符 temp = 14 << 2 temp = int num = 10; num = num + 1; double num = 1.0; num = num + 1.0; int num[10], *pNum = num; pNum = pNum + 1; operator overloading 运算符重载 56 pNum+1什么效果?指针值+1?

6 11.1 Introduction C++语言为了支持基本数据类型数据运算,内置了多种运算符,并且其中部分已针对操作数类型的不同进行了重载;
当需要将这些运算符用于用户自定义类型时,用户可进行运算符重载。 重载运算符的基本概念、限制,何时选择重载? 如何实现重载?全局 vs 成员函数 拷贝构造函数 / 转换构造函数 自定义String类 vs 标准string类

7 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

8 11.2 Fundamentals & Restrictions --- 需求
2018年11月30日5时32分 11.2 Fundamentals & Restrictions 需求 目的:提高类代码的可用、可读性 HugeintA.add(HugeIntB) vs HugeintA + HugeintB 提高C++的可扩展性 特别适合于和数学相关的类 复数类 大整数类 多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。

9 11.2 Fundamentals & Restrictions --- 语法
运算符重载只是一种“语法上的方便”,也就是说它只是另一种函数调用的方式。区别: 定义方式 调用方式 定义重载的运算符(可视为特殊函数)就像定义函数(全局/成员),区别是该函数的名称是 HugeInt operator+(const HugeInt& a);

10 11.2 Fundamentals & Restrictions --- 语法
运算符重载只是一种“语法上的方便”,也就是说它只是另一种函数调用的方式。区别: 定义方式 调用方式 普通函数 全局函数: 函数名(参数列表) 类成员函数: 对象.函数名(参数列表)等 重载的运算符 使用时以表达式形式出现: HugeIntA + HugeIntB

11 11.2 Fundamentals & Restrictions --- 限制
2018年11月30日5时32分 11.2 Fundamentals & Restrictions 限制 To use an operator on class objects, that operator must be overloaded with three exceptions(针对对象,有三个运算符不用重载): assignment operator (=) address operators (&) comma operators (,) a=(b=2,c=7,d=5), a1=(++b,c--,d+3); a2=++b,c--,d+3;

12 11.2 Fundamentals & Restrictions --- 限制

13 11.2 Fundamentals & Restrictions --- 限制
重载运算符应该仿效其相应的内置对象的功能 重载成的成员函数必须是非static的(带着问题思考) HugeInt operator+(const HugeInt& a); 不能更改Precedence(优先级), Associativity(结合律) 以及 Number of Operands(操作数数目) 仅能重载现有运算符,不能创造新运算符

14 11.2 Fundamentals & Restrictions --- 限制 continue
仅能重载应用于用户定义数据类型操作数的运算符 int + int X Hugeint + Hugeint √ Hugeint + int √ int + Hugeint √ 运算符必须显性重载 重载+和=不代表重载了+=

15 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

16 11.3 Operator Functions as Class Members vs. Global Functions
运算符函数可以是成员函数或者全局函数 当重载为类的成员函数时 非静态的类成员函数 class HugeInt { public: HugeInt operator+( int ); }; 使用this指针隐性获取操作左值 左操作数(或唯一的操作数)必须为该类对象(或对象引用) 将自动包含该类对象(或其引用)作为操作数,函数参数个数等于运算符目数-1

17 11.3 Operator Functions as Class Members vs. Global Functions
2018年11月30日5时32分 11.3 Operator Functions as Class Members vs. Global Functions 当重载为全局函数时 形式 class HugeInt { friend HugeInt operator+(const HugeInt &, int); }; 函数参数个数等于运算符的目数 全局函数: 是否访问私有数据 Friend(可访问私有数据)复习~~~ Non-friend 思考:必须重载为全局函数的情况 左操作数必须为一个基本类型对象 使运算符具有可交换性 HugeInteger + int 和 int + HugeInteger 成员函数+参数调换的全局函数 实例

18 11.3 Operator Functions as Class Members vs. Global Functions (对比)
HugeIntA + intA HugeIntA.operator+( intA ); operator+( HugeIntA, intA ); class HugeInt { friend HugeInt operator+( const HugeInt &, int ); }; class HugeInt { public: HugeInt operator+( int ); };

19 11.3 Operator Functions as Class Members vs. Global Functions
intB + HugeIntB intB.operator+( HugeIntB ); operator+( intB, HugeIntB ); class HugeInt { friend HugeInt operator+( int, const HugeInt & ); };

20 11.3 Operator Functions as Class Members vs. Global Functions --- 设计原则
2018年11月30日5时32分 11.3 Operator Functions as Class Members vs. Global Functions 设计原则 ( ), [ ], ->和赋值(=, +=, -=等)运算符必须重载为成员函数 >>, <<和需要支持交换律(Commutative)的运算符重载为全局函数 其余运算符可以选择重载为成员或全局函数 他们或为一元操作符,如(),->,[],或为第一个参数必为类本身,如=,赋值操作符不可能左边的的赋给右边的,避免潜在的风险 如果赋值操作符可以作为全局函数重载的话,可能会出现表达错误的语句 如 int operator=(int a, integer b); 这样重载之后,语句 2 = a; 表述也是正确的,但是却是明显的语法错误 为了避免此类错误,需要将赋值操作符重载为成员函数 首先要知道,如果类中没有重载赋值操作符时,类会自动生成一个默认的赋值操作符。例如,有两个同类对象A和B,当你没有将赋值操作符重载,而进行 A=B 的操作时,编译器会自动调用赋值操作将B的数据成员拷贝到A中。 而如果你重载了一个全局的赋值操作符,那么编译器不知道是否还需要再自己合成一个赋值操作符,从而引发歧义。

21 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

22

23 11.4 Overloading Stream Insertion and Stream Extraction Operators
cout << phone; cout.operator<<( phone ); operator<<( cout, phone ); class PhoneNumber{ friend ostream &operator<<(ostream&, const PhoneNumber &) }; 程序解读 P 注意类头文件的友元声明、友元函数实现以及运算符调用

24 11.4 Overloading Stream Insertion and Stream Extraction Operators
2018年11月30日5时32分 11.4 Overloading Stream Insertion and Stream Extraction Operators cout << phone; 左值为ostream & cout << phone1 << phone2; cin >> phone; 左值为istream & cin >> phone1 >> phone2; 必须要定义为全局函数! 虚悬引用(针对临时对象返回引用) 注意<<和>>函数形参中PhoneNumber的不同类型(见图 行)

25 11.4 Overloading Stream Insertion and Stream Extraction Operators
存在隐患(>>)* 图11.4 从缓存中获取信息 采用setw及ignore函数精确控制 空格间隔多个输入 输入类型不正确 解决方法 判断cin流是否正常 if(cin) if(cin.fail()) 清空标志位及缓存 cin.clear();//清除错误状态 flushall();//清除缓存 输入:a 程序死循环运行

26 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

27 11.5 Overloading Unary Operators
2018年11月30日5时32分 11.5 Overloading Unary Operators 一元运算符重载 As a non-static member function with no arguments or as a global function with one argument; 不带参数的成员函数 带有一个参数的全局函数(参数为对象或对象引用)

28 11.5 Overloading Unary Operators
String s; !s判断是否为空字符串 operator!( s ); s.operator!(); class String { friend bool operator!( const String &); }; class String { public: bool operator!( ) const; };

29 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

30 11.6 Overloading Binary Operators
二元运算符重载 as a non-static member function with one argument or as a global function with two arguments 带有一个参数的成员函数 前提条件是仅当左操作数是该函数所在类的对象 带有二个参数的全局函数 one of those arguments must be either a class object or a reference to a class object 其中一个参数必须是对象或对象引用

31 11.6 Overloading Binary Operators
string1 < string2 operator<( string1, string2 ); string1.operator<(string2); class String { friend bool operator<( const String &, const String &); }; class String { public: bool operator<( const String &) const; };

32 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

33 需求分析 程序解读 P 11.6-11.8 写一个基于指针的Array 类,要求实现以下功能: 解决方法:
2018年11月30日5时32分 需求分析 写一个基于指针的Array 类,要求实现以下功能: 提供检查数组的下标的有效性(是否越界) 非字符数组可以使用cin/cout一次性输入/输出 对两个数组直接用“==”进行比较 数组作为参数时,不用传递数组大小(*) 一个数组可以用“=”赋给另一个数组(关于数组名) 解决方法: 在函数中检查下标范围 重载“<<”和“>>” 重载“==” 数组大小作为类的数据成员 重载“=” c和c++语言中数组下标越界,编译器是不会检查出错误的,但是实际上后果可能会很严重,比如程序崩溃等,所以在日常的编程中,程序员应当养成良好的编程习惯,避免这样的错误发生。 程序解读 P

34 Const类型的返回值表示对相关对象的保护,实现禁左。禁止(a=b)=c
class Array { friend ostream &operator<<( ostream &, const Array & ); friend istream &operator>>( istream &, Array & ); public: Array( int = 10 ); Array( const Array & ); ~Array(); int getSize() const; const Array &operator=( const Array & ); bool operator==( const Array & ) const; bool operator!=( const Array &right ) const return ! ( *this == right ); } int &operator[]( int ); int operator[]( int ) const; private: int size; int *ptr; }; 为何要重载拷贝构造函数? 为何返回值要加&和const? &为了在a=(b=c)时提高性能 Const类型的返回值表示对相关对象的保护,实现禁左。禁止(a=b)=c

35 &:避免递归调用 const:避免修改源对象, 支持复制const对象 为什么重载? 返回值 const &? 参数为什么是引用类型?
Array::Array(const Array &arrayToCopy):size(arrayToCopy.size) { ptr = net int [size]; for(int i = 0; i < size; i++) ptr[i] = arrayToCopy.ptr[i]; } Array::~Array() { delete [] ptr;} Const Array &Array::operator=( const Array &right) if(&right != this) if(size != right.size) delete [] ptr; size = right.size; ptr = new int [size]; for(int i = 0; i < size; i++) ptr[i] = right.ptr[i]; return * this; &:避免递归调用 const:避免修改源对象, 支持复制const对象 为何要自定义析构函数? 为什么重载? 返回值 const &? 参数为什么是引用类型? 为什么检查“自我赋值”?

36 返回&:支持左值(可以被赋值) 注意同上面函数的区别 全局函数 int &Array::operator[](int subscript)
{ …… return ptr[subscript]; } int Array::operator[](int subscript) const istream &operator>>(istream &input, Array &a) return input; ostream &operator<<(ostream &output, const Array &a) return output; 返回&:支持左值(可以被赋值) 注意同上面函数的区别 全局函数

37 先计算返回int,然后进行<<运算
int main() { Array integers1( 7 ); Array integers2; cout << integers1.getSize() << integers1; cout << integers2.getSize() << integers2; cin >> integers1 >> integers2; cout << integers1 << integers2; Array integers3( integers1 ); // Array integers3 = integers1; cout << integers3.getSize() << integers3; integers1 = integers2; cout << integers1 << integers2; cout << "\nintegers1[5] is " << integers1[ 5 ]; integers1[ 5 ] = 1000; cout << integers1; integers1[ 15 ] = 1000; // ERROR: out of range return 0;} 先计算返回int,然后进行<<运算 会调用哪个[]函数?

38 总结 Copy constructor “=”重载 “[ ]”的重载 “>>”和“<<”的重载 避免递归调用
2018年11月30日5时32分 总结 Copy constructor 参数为什么是const &类型? “=”重载 为什么重载? 返回值 const &? 参数为什么是引用类型? 为什么检查“自我赋值”? “[ ]”的重载 Line88和Line103的区别? 整数、浮点数、字符(串)等 “>>”和“<<”的重载 为什么返回为引用类型? 避免递归调用 避免修改源对象 支持复制const对象

39 总结——进一步讨论 虚悬指针(产生原因)与指针回收问题 通常会为任何一个使用动态分配内存的类同时提供一组函数(重要!):
2018年11月30日5时32分 总结——进一步讨论 虚悬指针(产生原因)与指针回收问题 关于delete之后的指针使用(有的编译器可以访问,但会有潜在的错误) 通常会为任何一个使用动态分配内存的类同时提供一组函数(重要!): 复制构造函数 析构函数 重载的赋值运算符函数 如何阻止类对象的复制 将重载的赋值运算符以及拷贝构造函数设置为private

40 总结 原则:重载的运算符和已经存在的运算符在使用规则上没有区别 运算符重载什么情况下返回引用类型,什么情况下返回数值类型?
返回值还要继续被处理的情况: 返回引用类型 例如:+=运算符 a += b += c; return *this; 返回值用过就丢弃的情况: 返回值类型 例如:+运算符 a = (b + c) * d;

41 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

42 11.8 Converting between types(类型转换)
Fundamental types(基本数据类型) int x; char y = static_cast<char>(x); Or char y = (char)x; User-defined types(用户自定义类型) 转换构造函数 强制类型转换

43 (编译器可以隐性调用这些函数创建临时对象)
对象转换 同类对象转换 非同类对象转换 初始化对象 拷贝构造函数 转换构造函数 重载强制类型转换运算符 (编译器可以隐性调用这些函数创建临时对象)

44 11.8 Converting between types(类型转换)
转换构造函数 Conversion Constructor 单实参的构造函数,用于将其他类型的对象(包括基本数据类型)转换为当前类的对象 任何单参数构造函数都可看做是转换构造函数 目的:使编译器自动执行类型转化!(隐式调用) 基本数据类型之间(char->int) 抽象数据类型之间(Time->Date) 抽象数据类型和基本数据类型之间(int->HugeInt)

45 11.8 Converting between types(类型转换)
class One{ public: One() { cout << "One Constructor called." << endl; } ~One() { cout << "One Destructor called." << endl; } }; class Two{ Two( const One & ) { cout << "Conversion Constructor called." << endl; } ~Two() { cout << "Two Destructor called." << endl; } void f( Two ){ cout << "Function f called." << endl; } int main() { One one; f(one); cout << "Check whether Two has been destructed?" << endl; return 0; } 转换构造函数,参数为其它数据类型 One Constructor called. Conversion Constructor called. Function f called. Two Destructor called. Check whether Two has been destructed? One Destructor called. 隐式调用

46 11.8 Converting between types(类型转换)
强制类型转换函数的重载 函数声明形式 A::operator int() const; // 不需要返回值,不修改原对象 实现:A -> int A::operator OtherClass() const; 实现:A-> OtherClass 用户调用: A s; static_cast<int>(s); static_cast<OtherClass>(s); 编译器调用: s.operator int(); s.operator OtherClass();

47 cout<<static_cast<int>(s); return 0; }
2018年11月30日5时32分 class A {public: A(int=5); A( const A & ); ~A(); int getSize() const; operator int() const; int *ptr; int size; }; A::operator int() const { int temp=0; for ( int i = 0; i < size; i++ ) temp=temp+ptr[i]*pow(10,i); return temp; } ; int main() { A s; cout<<static_cast<int>(s); return 0; } int main() { A s; cout<<s; return 0; } 有问题! 隐式调用 此时不需重载<<

48 11.8 Converting between types(类型转换)
总结 转换构造函数 重载强制类型转换运算符 隐式调用原则: 先查找符合要求的函数 再调用转换运算 转换运算不能级联调用(仅能调用一次)

49 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

50 需求:实现一个String类,对字符串操作
用已有字符串初始化当前字符串 定义拷贝构造函数 私有数据成员:int length; char *sPtr;需要哪些重载函数? 需要重载赋值运算符 提供取子串功能,如何重载运算符? 重载函数调用运算符“()” () – 函数调用运算符function call operator, can take arbitrarily long and complex parameter lists. 对象名(参数列表); //可以重载多种不同的情况 例:string1(2,2)//取string1从第二个位置开始的两个字符 程序解读 P

51 class String { friend ostream &operator<<( ostream &, const String & ); friend istream &operator>>( istream &, String & ); public: String( const char * = "" ); String( const String & ); ~String(); const String &operator=( const String & ); const String &operator+=( const String & ); bool operator!() const; bool operator==( const String & ) const; bool operator<( const String & ) const; bool operator!=( const String &right ) const return !( *this == right ); } bool operator>( const String &right ) const return right < *this; bool operator<=( const String &right ) const { return !( right < *this ); } bool operator>=( const String &right ) const return !( *this < right ); char &operator[ ]( int ); char operator[ ]( int ) const; String operator()( int, int = 0 ) const; int getLength() const; private: int length; char *sPtr; void setString( const char * ); }; #endif

52 #include <iomanip> using std::setw; #include <cstring>
2018年11月30日5时32分 #include <iomanip> using std::setw; #include <cstring> using std::strcmp; using std::strcpy; using std::strcat; #include <cstdlib> using std::exit; #include "String.h" String::String( const char *s ) : length( ( s != 0 ) ? strlen( s ) : 0 ) { cout << "Conversion (and default) constructor: " << s << endl; setString( s ); } String::String( const String &copy ) : length( copy.length ) { cout << "Copy constructor: " << copy.sPtr << endl; setString( copy.sPtr ); String::~String() { cout << "Destructor: " << sPtr << endl; delete [ ] sPtr; void String::setString( const char *string2 ) { sPtr = new char[ length + 1 ]; if ( string2 != 0 ) strcpy( sPtr, string2 ); else sPtr[ 0 ] = '\0'; } const String &String::operator=( const String &right ) { cout << "operator= called" << endl; if ( &right != this ) // avoid self assignment delete [ ] sPtr; // prevents memory leak length = right.length; setString( right.sPtr ); cout << "Attempted assignment of a String to itself" << endl; return *this; } \0是C++中字符串的结尾标志,存储在字符串的结尾。比如char cha[5]表示可以放4个字符的数组,由于c/c++中规定字符串的结尾标志为'\0',它虽然不计入串长,但要占内存空间,而一个汉字一般用两个字节表示,且c/c++中如一个数组cha[5],有5个变量,分别是 cha[0] , cha[1] , cha[2] , cha[3] , cha[4] , 所以cha[5]可以放4个字母(数组的长度必须比字符串的元素个数多1,用以存放字符串结束标志'\0')或者放2个汉字(1个汉字占2个字节,1个字母占一个字节),cha[5]占5个字节内存空间。

53 const String &String::operator+=( const String &right ) {
2018年11月30日5时32分 const String &String::operator+=( const String &right ) { int newLength = length + right.length; char *tempPtr = new char[ newLength + 1 ]; strcpy( tempPtr, sPtr ); strcpy( tempPtr + length, right.sPtr ); delete [] sPtr; sPtr = tempPtr; length = newLength; return *this; } bool String::operator!() const return length == 0; bool String::operator==( const String &right ) const return strcmp( sPtr, right.sPtr ) == 0; bool String::operator<( const String &right ) const return strcmp( sPtr, right.sPtr ) < 0; char &String::operator[ ]( int subscript ) { if ( subscript < 0 || subscript >= length ) { cerr << "Error: Subscript " << subscript << " out of range" << endl; exit( 1 ); //退出整个程序,返回值为1 } return sPtr[ subscript ]; char String::operator[ ]( int subscript ) const // 右值 { if ( subscript < 0 || subscript >= length ) cerr << "Error: Subscript " << subscript exit( 1 ); } int strcmp(const char *src,const char *dst)  {      int i = 0;      while(src[i] && dst[i])          {          if(src[i] > dst[i])              return 1;              else                  if(src[i] < dst[i])                      return -1;                      else                          i++;                              }      return 0; 

54 String String::operator() ( int index, int subLength ) const {
if ( index < 0 || index >= length || subLength < 0 ) return ""; int len; if ( ( subLength == 0 ) || ( index + subLength > length ) ) len = length - index; else len = subLength; char *tempPtr = new char[ len + 1 ]; strncpy( tempPtr, &sPtr[ index ], len ); tempPtr[ len ] = '\0'; String tempString( tempPtr ); delete [ ] tempPtr; return tempString; } int String::getLength() const { return length; } ostream &operator<<( ostream &output, const String &s ) { output << s.sPtr; return output; istream &operator>>( istream &input, String &s ) char temp[ 100 ]; input >> setw( 100 ) >> temp; s = temp; return input; //转换构造函数+ ’=’的重载

55 11.9 Case Study: String Class
copy constructor 例1:String s1(s2); conversion constructor 例2:String s3(“hello”); 例3:myString = “hello”; Step1.调用转换构造函数,生成一个临时对象; Step2.调用重载的赋值运算

56 cout << "\n\ns1 += \" to you\" yields" << endl;
cout << "s1 = " << s1 << "\n\n"; cout << "The substring of s1 starting at\n" << "location 0 for 14 characters, s1(0, 14), is:\n" << s1( 0, 14 ) << “\n\n”; cout << "The substring of s1 starting at\n" << s1( 15 ) << "\n\n"; String *s4Ptr = new String( s1 ); cout << “\n*s4Ptr = ” << *s4Ptr ; cout << "assigning *s4Ptr to *s4Ptr" ; *s4Ptr = *s4Ptr; cout << "*s4Ptr = " << *s4Ptr << endl; delete s4Ptr; s1[ 0 ] = 'H'; s1[ 6 ] = 'B'; s1[ 30 ] = 'd'; // ERROR: subscript out of range return 0; } int main() { String s1( "happy" ); String s2( " birthday" ); String s3; cout << boolalpha << "\n\nThe results of comparing s2 and s1:" << "\ns2 == s1 yields " << ( s2 == s1 ) << "\ns2 != s1 yields " << ( s2 != s1 ) << "\ns2 > s1 yields " << ( s2 > s1 ) << "\ns2 < s1 yields " << ( s2 < s1 ) << "\ns2 >= s1 yields " << ( s2 >= s1 ) << "\ns2 <= s1 yields " << ( s2 <= s1); if ( !s3 ) { s3 = s1; cout << "s3 is \"" << s3 << "\""; } s1 += s2; cout << s1;

57 int main() { String s1( "happy" ); String s2( " birthday" ); String s3; cout << boolalpha << "\n\nThe results of comparing s2 and s1:" << "\ns2 == s1 yields " << ( s2 == s1 ) << "\ns2 != s1 yields " << ( s2 != s1 ) << "\ns2 > s1 yields " << ( s2 > s1 ) << "\ns2 < s1 yields " << ( s2 < s1 ) << "\ns2 >= s1 yields " << ( s2 >= s1 ) << "\ns2 <= s1 yields " << ( s2 <= s1); if ( !s3 ) { s3 = s1; cout << "s3 is \"" << s3 << "\""; } s1 += s2; cout << s1; cout << "\n\ns1 += \" to you\" yields" << endl; s1 += " to you"; cout << "s1 = " << s1 << "\n\n"; cout << "The substring of s1 starting at\n" << "location 0 for 14 characters, s1(0, 14), is:\n" << s1( 0, 14 ) << “\n\n”; //调用哪些函数? cout << "The substring of s1 starting at\n" << s1( 15 ) << "\n\n"; String *s4Ptr = new String( s1 ); cout << “\n*s4Ptr = ” << *s4Ptr ; cout << "assigning *s4Ptr to *s4Ptr" ; *s4Ptr = *s4Ptr; cout << "*s4Ptr = " << *s4Ptr << endl; delete s4Ptr; s1[ 0 ] = 'H'; s1[ 6 ] = 'B'; s1[ 30 ] = 'd'; // ERROR: subscript out of range return 0; }

58 cout << "\n\ns1 += \" to you\" yields" << endl;
cout << "s1 = " << s1 << "\n\n"; cout << "The substring of s1 starting at\n" << "location 0 for 14 characters, s1(0, 14), is:\n" << s1( 0, 14 ) << “\n\n”; //调用哪些函数? cout << "The substring of s1 starting at\n" << s1( 15 ) << "\n\n"; String *s4Ptr = new String( s1 ); cout << “\n*s4Ptr = ” << *s4Ptr ; cout << "assigning *s4Ptr to *s4Ptr" ; *s4Ptr = *s4Ptr; cout << "*s4Ptr = " << *s4Ptr << endl; delete s4Ptr; s1[ 0 ] = 'H'; s1[ 6 ] = 'B'; s1[ 30 ] = 'd'; // ERROR: subscript out of range return 0; }

59 11.9 Case Study: String Class
2018年11月30日5时32分 11.9 Case Study: String Class 临时对象问题 例:myString = “hello”; Step1.调用转换构造函数,生成一个临时对象; Step2.调用重载的赋值运算 类型转换 void f( Two ){ cout << "Function f called." << endl; } f(one); //Two的临时对象 函数返回对象 cout<<String(5,15); 无名临时对象 Integer &t = Integer(10); 等价于 Integer t(10); 在使用一个临时对象( 可能是无名对象 或者 返回对象值时 ) 创建构造另一个对象的过程的中,c++会优化掉该临时对象的产生,直接以相同参数调用相关构造函数构或者 直接调用拷贝构造函数 到 目标对象

60 11.9 Case Study: String Class
2018年11月30日5时32分 11.9 Case Study: String Class 函数返回对象时 // Code 1: Integer Func() { Integer itgr; return itgr; } void main() Integer in = Func(); Integer& iRef = Func(); cout<< iRef; // Code 2: Integer & Func() { Integer itgr; return itgr; } void main() Integer & in = Func(); cout<< in;//bad 表达式 Func() 处创建了一个临时对象,用来存储Func() 函数中返回的对象,临时对象由 Func() 中返回的 itgr 对象拷贝构造(值传递),临时对象赋值给 in后,赋值表达式结束,临时对象被析构。

61 如果一个临时对象被创建之后赋值给了一个引用,那么这个临时对象将被保存,否则将在当前代码行结束后析构
9:28:59 天使&海豚 2016/4/5 9:28:59 #include <iostream> using namespace std; class A { public: A(int a):x(a){cout<<"Constructor Called, and x = "<<x<<endl;} A(A &a):x(a.x){cout<<"Copy Constructor Called, and x = "<<x<<endl;} void print(){cout<<"print function called, and x = "<<x<<endl;} A ReturnA(){A a(1); return a;} A & ReturnRef(){A a(3); return a;} ~A(){cout<<"Deconstructor Called, and x = "<<x<<endl;} private: int x; }; void main(void) { A & ref = A(2); ref.print(); cout<<endl; A& refB = ref.ReturnA(); refB.print(); cout<<endl; ref.ReturnA(); cout<<endl; A& refC = ref.ReturnRef(); refC.print(); } 如果一个临时对象被创建之后赋值给了一个引用,那么这个临时对象将被保存,否则将在当前代码行结束后析构

62 千万不要返回临时对象和局部对象的引用! // Code 2:
Complex &Complex::operator+( const Complex &operand2 ) const { return Complex( real + operand2.real, imaginary + operand2.imaginary ); } 千万不要返回临时对象和局部对象的引用! // Code 3: Complex &Complex::operator+( const Complex &operand2 ) const { Complex temp; temp= Complex( real + operand2.real, imaginary + operand2.imaginary ); return temp; }

63 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

64 11.10 Standard Library Class string(自学,同String的区别)
three member functions of standard class string - empty, substr and at that were not part of our String example Header file: <string> 程序解读 11.15

65 int main() { string s1( "happy" ); string s2( " birthday" ); string s3; cout << "s1 is \"" << s1 << "\"; s2 is \"" << s2 << "\"; s3 is \"" << s3 << '\"' ; if ( s3.empty() ) { cout << "s3 is empty; assigning s1 to s3;" << endl; s3 = s1; } s1 += s2; cout << s1; s1 += " to you"; cout << "s1 = " << s1 << "\n\n"; cout << "The substring of s1 starting at location 0 for\n" << "14 characters, s1.substr(0, 14), is:\n" << s1.substr( 0, 14 ) << "\n\n"; s1.substr( 15 ) << endl; string *s4Ptr = new string( s1 ); cout << "\n*s4Ptr = " << *s4Ptr << "\n\n"; cout << "assigning *s4Ptr to *s4Ptr" << endl; *s4Ptr = *s4Ptr; //测试自我赋值 cout << "*s4Ptr = " << *s4Ptr << endl; delete s4Ptr; s1[ 0 ] = 'H'; s1[ 6 ] = 'B'; cout << "\ns1 after s1[0] = 'H' and s1[6] = 'B' is: “ << s1 << "\n\n"; s1.at( 30 ) = 'd'; // ERROR: subscript out of range return 0; }

66 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

67 11.11 Overloading ++ and – – 前置自增(自减) 后置自增(自减) 目标:实现对用户自定义类型对象的自增与自减
++a;--a; 后置自增(自减) a++;a--; 目标:实现对用户自定义类型对象的自增与自减 date ++;

68 Date &operator++(Date &);
11.11 Overloading ++ and – – ++d1 (前置自增) 全局函数 成员函数 d1.operator++() operator++(d1) Date &operator++(); Date &operator++(Date &);

69 Date operator++(int); Date operator++(Date &, int);
11.11 Overloading ++ and – – d1++ (后置自增) 全局函数 成员函数 d1.operator++(0) operator++(d1,0) Date operator++(int); Date operator++(Date &, int);

70 Class C{ friend ostream & operator<<(ostream &output, const C&c); int value; Public: C() {value = 0;} C& operator++() {value ++; return *this;}//前置 C operator++(int) //后置 { C temp = *this; ++(*this); return temp; } }; ostream & operator<<(ostream &output, const C&c) output<<c.value; return output;

71 临时对象 int main() { C c1; cout<<c1++<<endl;
cout<<"++c1++ = "<< ++c1++<<endl cout<<"current c1 = "<<c1<<endl; return 0; } 临时对象

72 说明:后置自增(自减)运算符需要创建临时对象,将对性能造成影响,特别是在循环中使用时,应尽量减少使用

73 Topics 11.1 Introduction 11.2 Fundamentals & Restrictions
11.3 Operator Functions as Class Members vs. Global Functions 11.4 Overloading Stream Insertion and Stream Extraction Operators 11.5 Overloading Unary Operators 11.6 Overloading Binary Operators 11.7 Case Study: Array Class 11.8 Converting between types 11.9 Case Study: String Class 11.10 Standard Library Class string(self study) 11.11 Overloading ++ and ––(self study) 11.12 Case Study: A Date Class(self study)

74 Summary 哪些运算符可以重载?何时需要重载?有何限制?如何重载? 成员函数 vs 全局函数 拷贝构造函数和转换构造函数
“.”,”.*”,”::”,”?:”不能重载 “=“,”&”,”,”可以直接使用但有时也需要重载 成员函数 vs 全局函数 ( ), [ ], ->和赋值(=, +=, -=等)运算符必须重载为成员函数(有时函数需要被重载为常成员函数) 支持交换律的运算符必须重载为全局函数 拷贝构造函数和转换构造函数 自定义String类 vs 标准string类

75 Summary “=“ “<<” and “>>” 拷贝构造函数
const Array &operator=( const Array & ); “<<” and “>>” friend ostream &operator<<(ostream&,const Array &) friend istream &operator>>(istream&, Array &) 拷贝构造函数 Num (const Num & n);

76 Homework! 实验必选题目(交实验报告): 11.13,11.14,11.15,11.17

77 Return int main() { Array integers1( 7 ); Array integers2;
const Array integers4=integers2; cout << “integers4:\n” <<integers4[0]; //调用const运算符函数 [ ] if ( integers1 != integers2 ) cout << "integers1 and integers2 are not equal" << endl; Array integers3( integers1 ); cout << integers3.getSize()<< integers3; integers1 = integers2; // note target Array is smaller cout << integers << integers2; if ( integers1 == integers2 ) cout << "integers1 and integers2 are equal" << endl; cout << "\nintegers1[5] is " << integers1[ 5 ]; integers1[ 5 ] = 1000; cout << "integers1:\n" << integers1; integers1[ 15 ] = 1000; // ERROR: out of range return 0;} Return

78 补充:cout和cerr的区别 Return cout可以重定向到一个文件中,cerr必须输出到显示器上 cerr的作用?
#include <iostream> using std::cout; using std::cin; using std::endl; using std::cerr; int main() {cout<<"hello world--cout"<<endl; cerr<<"hello world--cerr"<<endl; return 0; } cerr的作用? 当栈用完,无内存时,cerr可不经过缓冲区,直接向显示器输出信息。 C:\>test.exe>>out.txt Return

79 Return class HugeInt { friend HugeInt operator+(const HugeInt &, int);
public: HugeInteger( long = 0 ); // conversion/default constructor HugeInteger( const char * ); // copy constructor private: short integer[ 40 ]; }; HugeInt operator+(const HugeInt & add1, int add2) HugeInteger temp; value = add2; for(int index = 39 ; value != 0 && index >=0; index --) …… temp.interger[index] = add1.interger[index] + value%10; value /= 10; } Return error C2248: cannot access private member declared in class ‘HugeInt'

80 数组名代表了数组的首地址,在作为函数的参数的时候,会自动退化为指针
是const类型的指针,不能用在赋值运算符的左侧 int name[20]; int data; name = &data; Return

81 拷贝构造函数 Copy Constructor
int a = 10; //初始化 a = 100; //赋值 Num b; Num a = b; //拷贝构造,调用拷贝构造函数 a = b; //赋值

82 class Num{ public: Num(){ nums = new int[10]; for(int i=0; i<10; i++) nums[i] = i; } void setvalue(int idx, int v){ nums[idx] = v; } void print(){ cout << nums << ": "; for(int i=0; i<10; i++) cout << nums[i] << " "; cout << endl; ~Num(){ delete [ ] nums; } private: int *nums; }; int main() { Num a; a.setvalue(0, 100); a.print(); Num b = a; b.print(); return 0; } :

83 for(int i=0; i<10; i++) nums[i] = n.nums[i];
class Num{ public: ………… Num (const Num & n){ nums = new int[10]; for(int i=0; i<10; i++) nums[i] = n.nums[i]; cout << "Copy constructor called." << endl; } }; 拷贝构造函数,参数为同类对象const引用 : Copy constructor called. :

84 Return 拷贝构造函数 Copy Constructor,被调用时机:
传值方式传递对象参数 Num (Num n){…} 函数返回对象 Time Time::getTime(); 使用同类对象来初始化对象 Time t2 = t1; 举例:后两种情况混合使用 总结:当类中含有需要动态分配内存的指针数据成员,应提供拷贝构造函数并重载赋值运算符,以避免缺省拷贝和赋值. Return


Download ppt "Operator Overloading; String and Array Objects"

Similar presentations


Ads by Google