《C++大学教程》学习笔记(十)
2018-02-15 01:13
429 查看
《C++大学教程》学习笔记(十)
1.使用标准库中string类的重载运算符
下面这个实例给出了几个string类的重载运算符,事实上,几乎能想到的所有运算符都已经被string重载了。通过这个实例,我们可以对运算符重载有个初步的认识,下面是实例代码://使用string类中的重载运算符 #include <iostream> #include <string> using namespace std; int main(){ string s1("Happy "); string s2("birthday"); string s3; cout << "Original: " << "s1:" << s1 << " s2:"<< s2 << " s3:" << s3 << endl << endl; if (s1 < s2) //string类中重载的< cout << "s1 < s2" << endl; if (s1 != s3) //string类中重载的!= cout << "s1 != s3" << endl; s1 += s2; //string类中重载的+= cout << "s1 after += s2:" << s1 << endl; s1 += " to you"; //string类中重载的+= cout << "s1 after += \" to you\":" << s1 << endl << endl; s3 = s1.substr(0,14); //截取从0开始,长度为14的字符串 cout << "s3: " << s3 << endl; s3 = s1.substr(15); //截取从15开始剩余部分 cout << "s3: " << s3 << endl; }
测试结果如下图所示:
从运行结果可以发现,常用的运算符、比较符这些都已进行了重载,使用起来符合C++ 的习惯。
2.初入运算符重载
2.1一些要点
大部分的C++运算符都能被重载,但有一些不能,它们分别是:. .* :: ?:
这四种。
而当重载( )、[ ]、-> 和 任何赋值操作符( = )时,运算符重载函数必须被声明为类成员,即必须是成员函数。
2.2重载二元运算符
二元运算符可以重载为:带有一个参数的非static成员函数
带两个参数的非成员函数,一般为友元函数
下面我们以流输入与流输出运算符举例,给出一个重载 >> 与 << 的实例,具体的文件结构与代码如下所示:
PhoneNumber.h文件:
#ifndef PhoneNumber_h #define PhoneNumber_h #include <iostream> #include <string> using namespace std; class PhoneNumber{ //下面两个都是被声明为非成员的友元函数,用来重载>>与<< friend ostream &operator << (ostream &,const PhoneNumber &); //带const friend istream &operator >> (istream &,PhoneNumber &); //不需要const private: string areaCode; string exchange; string line; }; #endif /* PhoneNumber_h */
PhoneNumber.cpp文件:
#include "PhoneNumber.h" #include <iomanip> using namespace std; ostream & operator << (ostream &output,const PhoneNumber &number){ output << "(" << number.areaCode << ") " << number.exchange << "-" << number.line; return output; //返回类型为ostream的引用 } istream & operator >> (istream &input,PhoneNumber &number){ input.ignore(); //跳过( input >> setw(3) >> number.areaCode; input.ignore(); //跳过) input >> setw(3) >> number.exchange; input.ignore(); //跳过- input >> setw(4) >> number.line; return input; //返回类型为istream的引用 }
main.cpp文件:
#include "PhoneNumber.h" #include <iostream> using namespace std; int main(){ PhoneNumber phone; cout << "Enter phone number in this form:(123) 456-7890:\n"; cin >> phone; //调用的是自定义的 >> cout << "The phone number entered is: " << phone << endl; //调用的是自定义的 << return 0; }
运行结果如下图所示:
在上面这个实例中,当编译器遇到了
cin >> phone;
这句表达式时,它实际上产生的是这样的调用:
operator >> (cin , phone);
这显然是一个声明为非成员函数的友元函数(带两个参数),那么我们能不能把这个符号重载函数声明为一个成员函数(带一个参数)呢?
理论上是可行的,但如果那么做,会产生一种十分尴尬的现象,比如当我们想用自定义的 << 或 >> 时,就得采用如下这种格式:
phone << cout; phone >> cin;
这种怪异的形式显然与C++的使用习惯不同。
因此,二元运算符的重载运算符 可以作为 成员函数 来实现的前提条件是 仅当左操作数是该函数所在类的对象。
在上面这个实例中,无论是 >> 还是 << ,其左操作数都是 输入(istream) 或 输出(ostream) ,右操作数才是函数所在类的对象,所以不能采用成员函数的方式来重载。
2.3重载一元运算符
同理,一元运算符可以重载为:不带参数的非static成员函数
带一个参数的非成员函数,一般为友元函数
下面我们一样以一个实例来介绍如何重载一元运算符,并着重探究了重载一元运算符中前置++与后置++的区别,下面是文件结构和具体代码:
Date.h文件:
#ifndef Date_h #define Date_h #include <iostream> #include <array> using namespace std; class Date{ friend ostream & operator << (ostream &,const Date &); //用友元函数重载一个 << 运算符 public: Date(int m = 1,int d = 1,int y = 1900); void setDate(int,int,int); Date &operator++(); //重载前置++ Date operator++(int); //重载后置++,注意返回类型没有引用 Date &operator+=(unsigned int); //重载+= static bool leapYear(int); //判断某一年是否是闰年 bool endOfMonth(int) const; //判断某一天是否是月底 private: unsigned int month; //1 - 12 unsigned int day; //1 - 31 based on month unsigned int year; static const array<unsigned int, 13> days; void helpIncrement(); //完成加一天的函数 }; #endif /* Date_h */
Date.cpp文件:
#include "Date.h" #include <iostream> #include <string> using namespace std; const array<unsigned int, 13> Date::days = {0,31,28,31,30,31,30,31,31,30,31,30,31}; //注意这里的days一定要声明是Date作用域的 Date::Date(int m,int d,int y){ setDate(m, d, y); } void Date::setDate(int mm, int dd, int yy){ if (mm >= 1 && mm <= 12) month = mm; else throw invalid_argument("Month must be 1-12"); if (yy >= 1900 && mm <= 2100) year = yy; else throw invalid_argument("Year must be 1900-2100"); if ((dd >= 1 && dd <= days[month]) || (month == 2 && dd == 29 && leapYear(year))) day = dd; else throw invalid_argument("Day is out of range"); } //前置++的重载函数 Date & Date::operator++(){ helpIncrement(); //先加 return *this; //返回加完后的对象 } //后置++的重载函数,int不用管,只是用来代表这是后置++ Date Date::operator++(int){ Date temp = *this; //先将当前对象拷贝到一个临时对象 helpIncrement(); //后加 return temp; //返回临时对象 } Date & Date::operator+=(unsigned int addDays){ for (int i = 0; i < addDays; i++) helpIncrement(); return *this; } bool Date::leapYear(int testYear){ if ((testYear % 400 == 0) || (testYear % 4 == 0 && testYear % 100 != 0)) return true; else return false; } bool Date::endOfMonth(int testDay) const{ if (month == 2 && leapYear(year)) return testDay == 29; else return testDay == days[month]; } void Date::helpIncrement(){ if (!endOfMonth(day)) //如果不是当前月的最后一天 day++; else if (month < 12){ //如果是当前月的最后一天且不是12月31号 month++; day = 1; } else{ //12月31号 year++; month = 1; day = 1; } } ostream & operator << (ostream &output,const Date &date){ output << date.month << "/" << date.day << "/" << date.year; return output; }
main.cpp文件:
#include <iostream> #include "Date.h" using namespace std; int main(){ Date d1(12,27,2010); Date d2; cout << "d1 is: " << d1 << "\nd2 is: " << d2 ; cout << "\n\nd1 += 7 is: " << (d1 += 7); d2.setDate(2, 28, 2008); cout << "\n\nd2 is: " << d2; cout << "\n++d2 is: " << ++d2; Date d3(7,13,2010); cout << "\n\n测试前置++\nd3 is: " << d3; cout << "\n++d3 is: " << ++d3; cout << "\nd3 is: " << d3; cout << "\n\n测试后置++\nd3 is: " << d3; cout << "\nd3++ is: " << d3++; cout << "\nd3 is: " << d3 << endl; return 0; }
运行结果如下图所示:
从上面这个实例我们可以看出,重载前置++与后置++的声明及实现方式都是完全不同的。
对于前置++来说,通过成员函数来实现重载的函数原型是:
Date & operator++();
而对于后置++,其通过成员函数来实现重载的函数原型则是:
Date operator++( int ); //注意函数返回的类型没有引用
这里的int没有实际意义,只是为了让编译器能够区分前置++和后置++,就算是在这个函数的具体实现中也只用写一个int即可(即不需要定义局部变量)。
值得注意的是,由于后置++的函数按值返回一个Date对象,因此在具体实现中:
//后置++的重载函数,int不用管,只是用来代表这是后置++ Date Date::operator++(int){ Date temp = *this; //先将当前对象拷贝到一个临时对象 helpIncrement(); //后加 return temp; //返回临时对象 }
通常先返回一个包含对象原始值的临时对象(temp),这会对性能造成很大的影响。
出于这个原因,我们更倾向使用前置自增与自减运算符。
3.零零散散
通过new运算符为一个数组对象动态分配空间:int *ptr = new int[100]();
此语句分配了一个可以存储100个初始化为0的整数的数组,可以用下面的语句删除这个数组:
delete [] ptr;
explicit的意义:为了防止无意中(编译器一般默认)将单参数构造函数用作转换构造函数,我们在声明每个单参数构造函数的前面加了关键词explicit,其目的是禁止不应该允许的由构造函数完成的隐式转换。
>> 既可以作为按位右移运算符,也可以作为流提取(输入)运算符;
<< 既可以作为按位左移运算符,也可以作为流插入(输出)运算符。
相关文章推荐
- 《C++大学教程》学习笔记(一)
- 《C++大学教程》学习笔记(五)
- 《C++大学教程》学习笔记(六)
- 《C++大学教程》学习笔记(七)
- 《C++大学教程》学习笔记(二、三)
- 《C++大学教程》学习笔记(十六)
- 《C++大学教程》学习笔记(十四)
- 《C++大学教程》学习笔记 第四章:控制语句(第一部分)
- 《C++大学教程》学习笔记(八)
- 《C++大学教程》学习笔记 第五章:控制语句(第二部分)
- 《C++大学教程》学习笔记(十五)
- 《C++大学教程》学习笔记(四)
- 《C++大学教程》学习笔记(九)
- 《C++大学教程》学习笔记 第二章:C++编程入门
- 《C++大学教程》学习笔记(十一)
- 《C++大学教程》学习笔记(十二)
- 《80x86汇编语言程序设计》学习笔记(1)
- 《Python基础教程第二版》学习笔记(一)第一章 基础知识
- 学习笔记(1)
- <学习笔记> 安装Ionic