C++浓缩(七)

11 章 使用类

  • 运算符重载ios

  • 友元函数数组

  • 重载<<运算符,以便于输出函数

  • 状态成员学习

  • 使用rand生成随机值spa

  • 类的自动转换和强制类型转换指针

  • 类转换函数code

本章首先介绍运算符重载,容许标准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) {
    // 一、分钟相加
    // 二、分钟取模,结果为当前分钟
    // 三、分钟除法,若结果大于0,小时加1
    minutes += m;
    hours += minutes / 60;
    minutes = minutes % 60;
}
void Time::Time::AddHr(int h) {
    // 一、小时相加
    // 二、小时取模,结果为当前小时
    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类

解决办法:

  • 1、限制使用方式

  • 2、非成员函数

非成员函数不是由对象调用的,它使用的全部值都是显示参数。

这样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类对该运算符进行了重载,用做输出,能识别全部的基本类型;

一、<<的第一种重载版本

要使Time类知道使用cout,必须使用友元函数。由于第一个操做数不是Time。

void operatorMM(ostream & os, const Time & t)
{
     os << t.hours << " hours, " << t.minutes << " minutes";
}
cout << time;
相关文章
相关标签/搜索