您的位置:首页 > 编程语言 > C语言/C++

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 创建友元

第一步:将其原型放在类声明中,并加上关键字friend

friend 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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: