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;
相關文章
相關標籤/搜索