C++浓缩(七)
2016-04-05 00:00
197 查看
11 章 使用类
运算符重载友元函数
重载<<运算符,以便于输出
状态成员
使用rand生成随机值
类的自动转换和强制类型转换
类转换函数
本章首先介绍运算符重载,允许标准C++运算符用于类对象->友元,这种机制使得非成员函数可以访问私有数据->如何命令C++对类执行自动类型转换。
学习本章和12章将对类构造函数和类析构函数有更深入的了解。
11.1 运算符重载
之前介绍了函数重载,运算符重载是一种形式的C++多态。实际上,很多C++运算符已经被重载,比如*地址运算符和乘法,C++根据操作数的数目和类型来决定采用哪种操作。
可以定义一个表示数组的类,并重载+运算符。于是:
evening = sam + janet; // add two array objects;
要重载运算符,需遵守运算符函数的格式:
operator op(argument list)
eg:
operator +() operator *()
op必须是有效的C++运算符,不能虚构一个新的符号。
假设有一个Salesperson类,并重载了+运算符,使得两个对象的销售额相加:
districts = sid + sara; 相当于: districts = sid.operator+(sara);
然后该函数将隐式地使用sid(因为它调用了方法)而显式地使用sara对象(因为它被作为参数传递),来计算总和并返回。
11.2 计算时间,一个运算符重载示例
2小时35分钟和2小时40分钟相加的运算符重载代码示例,第7张通过定义结构相加来处理类似的情况,现在推广,采用类以及运算符重载来实现:
time.h #ifndef MYTIMED_H_ #define MYTIMED_H_ class Time { private: int hours; int minutes; public: Time(); Time(int h, int m = 0); void AddMin(int m); void AddHr(int h); void Reset(int h = 0, int m = 0); Time Sum(const Time & t) const; void Show() const; }; #endif ================ time.cpp #include<iostream> #include "time.h" Time::Time(){ hours = minutes = 0; } Time::Time(int h, int m) { hours = h; minutes = m; } void Time::Time::AddMin(int m) { // 1、分钟相加 // 2、分钟取模,结果为当前分钟 // 3、分钟除法,若结果大于0,小时加1 minutes += m; hours += minutes / 60; minutes = minutes % 60; } void Time::Time::AddHr(int h) { // 1、小时相加 // 2、小时取模,结果为当前小时 hours += h; hours = hours % 24; } void Time::Reset(int h, int m) { hours = h; minutes = m; } Time Time::Sum(const Time & t) const { Time sum; sum.minutes = minutes + t.minutes; sum.hours = hours + t.hours + sum.minutes/60; sum.minutes = sum.minutes % 60; // hours = hours % 24; return sum; } void Time::Show() const{ using std::cout; using std::endl; cout << "hours: " << hours <<endl; cout << "minutes: " << minutes <<endl; }
然而,返回值不能使引用,因为函数将创建一个新的Time对象。返回对象将创建对象的副本,而调用函数可以使用它。
然而,若返回类型为Time &,由于sum是局部变量,在函数结束时被删除,因此引用将指向一个不存在的对象。
使用返回类型Time意味着程序将在删除sum之前构造它的拷贝,调用函数将得到该拷贝。
usetime.cpp #include<iostream> #include"time.h" int main() { using namespace std; Time planning; Time coding(2, 20); Time fixing(4, 20); Time sum; cout << "planning: " << endl; planning.Show(); cout << "coding: " << endl; coding.Show(); cout << "fixing: " << endl; fixing.Show(); cout << "sum: " << endl; sum = coding.Sum(fixing); sum.Show(); return 0; }
11.2.1 添加加法运算符
只需将sum()的名称改成operator+()即可。这样,可以像调用Sum()那样来调用operator+()方法:sum = coding.operator+(fixing);
将该方法命令为operator+()后,也可以使用运算符表示法:sum = coding+fixing;
time.h中
Time operator+(const Time & t) const;
time.cpp中
Time Time::operator+(const Time & t) const {
usetime.cpp中
sum = coding+fixing;
总之,operator+()函数的名称使得可以使用函数表示法或运算符表示法来调用它。
可以将两个以上的对象相加吗?
t4 = t1 + t2 + t3;yes
11.2.2 重载限制
运算符重载的限制:至少有一个操作数是用户定义的类型;
使用运算符时不能违反运算符原来的句法规则;例如,不能将%重载成使用一个操作数;不能修改运算符的优先级;
不能创建新的运算符
不能重载下面的运算符
sizeof:sizeof运算符
.:成员运算符
*:成员指针运算符
:::作用域解析运算符
?::条件运算符
typeid:一个RTTI运算符
const_cast:强制类项转换运算符
dynamic_cast:强制类项转换运算符
reintcrpret_cast:强制类项转换运算符
static_case:强制类项转换运算符
表11.1中大多数运算符都可以通过成员或非成员函数进行重载,但下面的运算符只能通过成员函数进行重载:
=:赋值运算符
():函数调用运算符
[]:下标运算符
->:通过指针访问类成员的运算符
表11.1
。。。。。。。。
注意:在重载运算符时遵循一些明智的限制,eg:不能将*运算符重载成交换两个对象的数据成员。表示法中没有任何内容可以表明运算符完成的工作,因此最好定义一个其名称具有说明性的类方法,egswap();
11.2.3 其他重载运算符
比如乘法和减法;time.h Time operator-(const Time & t) const; Time operator*(double a) const; ======================== time.cpp Time Time::operator-(const Time & t) const { Time diff; int tol1; int tol2; tol1 = t.minutes + t.hours * 60; tol2 = minutes + hours * 60; diff.minutes = (tol2 - tol1) % 60; diff.hours = (tol2 - tol1) / 60; return diff; } Time Time::operator*(double a) const { Time aa; int total = hours * 60 * a + minutes * a; aa.minutes = total % 60; aa.hours = total / 60; return aa; } ======================== usetime.cpp cout << "diff: " << endl; diff = coding-fixing; diff.Show(); cout << "result: " << endl; result = coding * 2; result.Show();
11.3 友元
公有类方法提供私有部分的唯一访问途径,但这种限制太严格。C++提供了另一种形式的访问权限:友元。
友元函数;
友元类;
友元成员函数;
通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。只介绍友元函数,其他两种15章介绍。
为何需要友元?在为类重载二元运算符时常常需要友元。
例如,乘法运算符将一个Time值与一个double值结合在一起。这限制了该运算符的使用方式。
A\B为Time类
A = B * 2.75;//可以这么使用,相当于A = B.operator*(2.75);
A = 2.75 * B; //不可以,因为2.75不是Time类
解决办法:
一、限制使用方式
二、非成员函数
非成员函数不是由对象调用的,它使用的所有值都是显示参数。
这样A = 2.75 * B;
与下面的非成员函数匹配:
A = operator*(2.75, B);
该函数的原型如下:
Time operator*(double m, const Time & t);
对于非成员重载运算符函数来说,运算符表达式左边的操作数对应于运算符函数的第一个参数,运算符表达式右边的操作数对应于运算符函数的第二个参数。而原来的成员函数则按相反的顺序处理操作数。
使用非成员函数可以按所需的顺序获得操作数,但引发一个新问题:即非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。然而,有一个特殊的非成员函数可以访问类的私有成员,称为友元函数
11.3.1 创建友元
第一步:将其原型放在类声明中,并加上关键字friendfriend Time operator*(double m, const Time & t);
该原型意味着:
虽然operator*()函数是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用;
虽然operator*()不是成员函数,但它与成员函数的访问权限相同。
第二步:编写函数定义,不要使用Time::限定符,不要在定义中使用关键字friend
Time operator*(double m, const Time & t) { Time result; long totalminutes = (t.hours * 60 + t.minutes) * m; result.hours = totalminutes / 60; result.minutest =totalminutes % 60; return result; }
提示:如果要为类重载运算符,并将非类的项作为第一个操作数,则可以用友元函数来反转操作数的顺序。
11.3.2 常用的友元:重载<<运算符
可以对<<运算符重载,使之能与cout一起来显示对象的内容。eg:cout << time;
实际上,它已经被重载很多次了。
最初,<<是位运算符;
ofstream类对该运算符进行了重载,用作输出,能识别所有的基本类型;
1、<<的第一种重载版本
要使Time类知道使用cout,必须使用友元函数。因为第一个操作数不是Time。
void operatorMM(ostream & os, const Time & t) { os << t.hours << " hours, " << t.minutes << " minutes"; } cout << time;
相关文章推荐
- LeetCode之8_String to Integer (atoi)
- 大型分布式C++框架《一:框架简介》
- C++常用特性原理解析
- C++作业2
- C++动态分配二维数组
- C++虚函数和纯虚函数
- 记一个C++中的疑难杂症
- C++实验-标准体重
- C++预处理命令
- C++ 赋值运算符=重载
- 模拟ATM柜员机界面
- 个人学习C++过程中对const的总结:初始化系列之用字面值常量与其他类型的值初始化的区别(一)
- C++二进制兼容问题及解决方法
- C++ 文件操作
- C语言学习007:重定向标准输入和输出
- C++面向对象编程分享06----20160404_李楚煌
- C++ Primer 读书笔记-ch02
- 【day0404 C++】类的成员函数
- C++多态公有继承
- 为什么Windows 系统中用反斜杠表示路径,而C语言教材中都是用斜杠表示头文件的路径?