Chapter 7: 再論類別 目標 能夠動態地建構和解構物件 能夠指定 const(常數)物件和和 const 成員函式

Slides:



Advertisements
Similar presentations
C++语言程序设计教程 第5章 构造数据类型 第6章 C++程序的结构.
Advertisements

第一章 資料結構導論 1-1 資料結構簡介 1-2 認識程式設計 1-3 演算法效能分析 1-4 物件導向程式設計與Java.
Memory Pool ACM Yanqing Peng.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
Data Abstraction: The Walls
第八章 类和对象.
C++语言程序设计教程 第7章 类与对象 第7章 类与对象.
Chapter 7 Search.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
Linked List(串列) Why Linked List? Pointer Dynamic Allocation
Chapter 1 用VC++撰寫程式 Text book: Ivor Horton.
物件導向程式設計 (Object-Oriented rogramming)
内容提要 对象的生命周期 构造函数 析构函数 拷贝构造函数. 常宝宝 北京大学计算机科学与技术系
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
4.1 概述 4.2 类与对象的实现 4.3 对象的初始化和析构 4.4 类的包含 4.5 类模板
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
第五讲 数据的分组、合并与转换.
Chapter 5 – 指標和字串 目標: 能夠使用指標 能用指標藉著傳參照呼叫方式,將引數傳給函式 了解指標、陣列和字串間的密切關係
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
Classes Lecturer: 曾學文.
CHAPTER 9 建構方法 ROBERT.
C 程式設計— 指標.
Scope & Lifetime 前言 Local Scope Global Functions & Objects
第六章 类的扩展与继承.
程式敘述執行順序的轉移 控制與重複、方法 Lecturer:曾學文.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
C 程式設計— 指標 台大資訊工程學系 資訊系統訓練班.
Classes: A Deeper Look, Part 1
第六章 继承性和派生类 胡昊 南京大学计算机系软件所.
Classes: A Deeper Look, Part 2
创建型设计模式.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
Object-Oriented Programming:
類別樣板 Class Template 類似函式樣板 由類別樣板產生的類別稱為類別樣版的實體(instance)
Operator Overloading; String and Array Objects
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Object-Oriented Programming in C++ 第一章 C++的初步知识
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
前處理指令可以要求前處理器 (preprocessor) 在程式編譯之前,先進行加入其它檔案的內容、文字取代以及選擇性編譯等工作。
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
Object-Oriented Programming: Polymorphism
第九單元 Classes and data abstraction I
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
類別與物件 I (Classes and Objects I)
Abstract Data Types 抽象数据类型 Institute of Computer Software 2019/2/24
Classes (2) Lecture 7.
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
Chapter 2 & Chapter 3.
Speaker: Liu Yu-Jiun Date: 2009/4/29
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++复习2----类与对象.
Oop8 function函式.
第四章 栈和队列 栈 ( Stack ) 队列 ( Queue ) 优先队列 (Priority Queue) 小结.
Speaker: Liu Yu-Jiun Date: 2009/5/6
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
Inheritance -II.
第三章 数据抽象.
第 9 章 建構函式與解構函式.
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
方法進階及物件導向基礎 Lecturer: 楊昌樺.
第 5 章 继承、多态和虚函数 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
JAVA 程式設計與資料結構 第三章 物件的設計.
變數與資料型態  綠園.
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
Presentation transcript:

Chapter 7: 再論類別 目標 能夠動態地建構和解構物件 能夠指定 const(常數)物件和和 const 成員函式 了解夥伴(friend)函式與夥伴類別的目的 了解如何使用 static 資料成員與成員函式 了解容器(container)類別的概念 了解具有檢視容器類別元素功能的迭代(iterator)類別概念 了解指標 this 的用途

Chapter 7: 再論類別 本章綱要 7.1 簡介 Introduction 7.2 常數物件與常數成員函式 7.2 常數物件與常數成員函式 7.3 合成:將物件當作類別的成員 7.4 夥伴函式與夥伴類別 7.5 this 指標的使用 7.6 使用 new 與 delete 運算子的動態記憶體配置 7.7 static 類別成員 7.8 資料抽象化與資訊隱藏 範例:陣列、字串、佇列 7.9 容器類別和迭代子 7.10 代理類別

7.1 簡介 第六章到第八章討論的是屬於物件導向程式設計的基本觀念。 第九章與第十章討論的繼承(inheritance)與多型(polymorphism),這才算是真正物件導向程式設計。

7.2 const (常數) 物件與 const 成員函式 最小開放權限原則 Principle of least privilege 只給物件所需要的權限、不給過多的權力 關鍵字 const 說明這個物件不可以被修改 企圖修改這種物件的話會造成語法上的錯誤。 範例 const Time noon( 12, 0, 0 ); //可以作初值化 宣告類別 Time 的常數物件 noon,並作初值化成 12 點。 12 hour minute const Time noon(12,0,0) second Time( 12, 0, 0 );

7.2 const (常數) 物件與 const 成員函式 我們只能呼叫 const 物件的 const 函式。沒有修改資料成員的函式必須被宣告成 const 才能被 const 物件使用。 宣告成 const 的成員函式不可修改物件內容 const 必須被寫在函式原型與定義的地方 函式原型: ReturnType FunctionName(param1,param2…) const; 定義: ReturnType FunctionName(param1,param2…) const { …} 範例: int A::getValue() const { return privateDataMember }; const 函式可以傳回資料成員的值,但不能修改其內容。

7.2 const (常數) 物件與 const 成員函式 對變數作初值化、就會修改其內容 常見的程式設計錯誤 7.1-4 定義為 const 的成員函式若修改資料成員的內容,會造成語法錯誤。 const 的函式不可呼叫 non-const 函式,否則就造成語法錯誤。 透過 const 物件來呼叫非 const 函式,是語法錯誤。 企圖將建構子或解構子宣告為 const 是語法錯誤。

const 函式 非 const 函式 1 // Fig. 7.1: time5.h 2 // Declaration of the class Time. 3 // Member functions defined in time5.cpp 4 #ifndef TIME5_H 5 #define TIME5_H 6 7 class Time { 8 public: 9 Time( int = 0, int = 0, int = 0 ); // default constructor 10 11 // set functions 12 void setTime( int, int, int ); // set time 13 void setHour( int ); // set hour 14 void setMinute( int ); // set minute 15 void setSecond( int ); // set second 16 17 // get functions (normally declared const) 18 int getHour() const; // return hour 19 int getMinute() const; // return minute 20 int getSecond() const; // return second 21 22 // print functions (normally declared const) 23 void printMilitary() const; // print military time 24 void printStandard(); // print standard time 25 private: 26 int hour; // 0 - 23 27 int minute; // 0 - 59 28 int second; // 0 - 59 29 }; 30 31 #endif 非 const 函式 const 函式

建構子是非 const 但可以被 const 物件呼叫 32 // Fig. 7.1: time5.cpp 33 // Member function definitions for Time class. 34 #include <iostream> 35 36 using std::cout; 37 38 #include "time5.h" 39 40 // Constructor function to initialize private data. 41 // Default values are 0 (see class definition). 42 Time::Time( int hr, int min, int sec ) 43 { setTime( hr, min, sec ); } 44 45 // Set the values of hour, minute, and second. 46 void Time::setTime( int h, int m, int s ) 47 { 48 setHour( h ); 49 setMinute( m ); 50 setSecond( s ); 51 } 52 53 // Set the hour value 54 void Time::setHour( int h ) 55 { hour = ( h >= 0 && h < 24 ) ? h : 0; } 56 57 // Set the minute value 58 void Time::setMinute( int m ) 59 { minute = ( m >= 0 && m < 60 ) ? m : 0; } 60 61 // Set the second value 62 void Time::setSecond( int s ) 63 { second = ( s >= 0 && s < 60 ) ? s : 0; } 建構子是非 const 但可以被 const 物件呼叫

關鍵字 const 必須出現在函式定義與函式原型中 64 65 // Get the hour value 66 int Time::getHour() const { return hour; } 67 68 // Get the minute value 69 int Time::getMinute() const { return minute; } 70 71 // Get the second value 72 int Time::getSecond() const { return second; } 73 74 // Display military format time: HH:MM 75 void Time::printMilitary() const 76 { 77 cout << ( hour < 10 ? "0" : "" ) << hour << ":" 78 << ( minute < 10 ? "0" : "" ) << minute; 79 } 80 81 // Display standard format time: HH:MM:SS AM (or PM) 82 void Time::printStandard() // should be const 83 { 84 cout << ( ( hour == 12 ) ? 12 : hour % 12 ) << ":" 85 << ( minute < 10 ? "0" : "" ) << minute << ":" 86 << ( second < 10 ? "0" : "" ) << second 87 << ( hour < 12 ? " AM" : " PM" ); 88 } 關鍵字 const 必須出現在函式定義與函式原型中 非 const 函式不能被 const 物件使用,就算這些函式沒有修改資料內容也一樣。 (就像printStandard).

89 // Fig. 7.1: fig07_01.cpp 90 // Attempting to access a const object with 91 // non-const member functions. 92 #include "time5.h" 93 94 int main() 95 { 96 Time wakeUp( 6, 45, 0 ); // non-constant object 97 const Time noon( 12, 0, 0 ); // constant object 98 99 // MEMBER FUNCTION OBJECT 100 wakeUp.setHour( 18 ); // non-const non-const 101 102 noon.setHour( 12 ); // non-const const 103 104 wakeUp.getHour(); // const non-const 105 106 noon.getMinute(); // const const 107 noon.printMilitary(); // const const 108 noon.printStandard(); // non-const const 109 return 0; 110 } 產生編譯錯誤 Compiling... Fig07_01.cpp d:fig07_01.cpp(14) : error C2662: 'setHour' : cannot convert 'this' pointer from 'const class Time' to 'class Time &' Conversion loses qualifiers d:\fig07_01.cpp(20) : error C2662: 'printStandard' : cannot convert 'this' pointer from 'const class Time' to 'class Time &' Time5.cpp Error executing cl.exe.   test.exe - 2 error(s), 0 warning(s)

7.2 const (常數) 物件與 const 成員函式 軟體工程的觀點 7.3 const 成員函式可被重載為非 const 的版本,編譯器會依據呼叫此函式的物件是否為 const 來決定所要呼叫的函式是哪一個。 良好的程式設計習慣 7.1 將所有不需要修改資料內容的成員函式宣告為 const ,如此一來,就可以被所需要的 const 物件使用。 常數物件是在建構子設定完資料成員的內容後才成為不可改變的常數。 有些編譯器可以讓設定為常數的變數執行速度變得比較快。

7.2 const (常數) 物件與 const 成員函式 前面介紹的是 const 物件、const 函式,下面討論 const 資料成員,要怎麼作呢? 一個 const 變數只能在宣告時順便作初值化,宣告後就不能再改變它的內容了。但是類別中的資料成員在宣告時不能給初值,這怎麼辦呢? 下面的例子中 Increment 這個類別中有一個 increment 的 const 資料成員,其初值化必須在建構子作,寫法如下: Increment 的建構子寫法如下: Increment::Increment( int c, int i ) :increment( i ) { count = c; } : increment( i ) 將 increment 的初值設為 i

1 Increment Increment( 0,5 ): count(0), increment(1) count increment 1 // Fig. 7.4: fig07_04.cpp 2 // Using a member initializer to initialize a 3 // constant of a built-in data type. 4 #include <iostream> 5 6 using std::cout; 7 using std::endl; 8 9 class Increment { 10 public: 11 Increment( int c = 0, int i = 1 ); 12 void addIncrement() { count += increment; } 13 void print() const; 14 15 private: 16 int count; 17 const int increment; // const data member 18 }; 19 20 // Constructor for class Increment 21 Increment::Increment( int c, int i ) 22 : increment( i ) // initializer for const member 23 { count = c; } 24 25 // Print the data 26 void Increment::print() const 27 { 28 cout << "count = " << count 29 << ", increment = " << increment << endl; 30 } 31 32 int main() 33 { 1 count increment Increment Increment( 0,5 ): count(0), increment(1) 若用設定敘述式來作 increment 的初值化設定 (例如 increment = i )會造成語法錯誤

10 5 25 20 15 Increment value(10,5) Increment( 10,5 ): 35 36 cout << "Before incrementing: "; 37 value.print(); 38 39 for ( int j = 0; j < 3; j++ ) { 40 value.addIncrement(); 41 cout << "After increment " << j + 1 << ": "; 42 value.print(); 43 } 44 45 return 0; 46 } 10 5 count increment Increment value(10,5) Increment( 10,5 ): count(10), increment(5) 25 20 15 Before incrementing: count = 10, increment = 5 After increment 1: count = 15, increment = 5 After increment 2: count = 20, increment = 5 After increment 3: count = 25, increment = 5

1 // Fig. 7.5: fig07_05.cpp …… 9 class Increment { 11 public: 22 private: 23 int count; 24 const int increment; 26 }; 29 Increment::Increment( int c, int i ) { 31 count = c; 32 increment = i; // ERROR: Cannot modify a const object 34 }

7.2 const (常數) 物件與 const 成員函式 實際上每個資料成員都可以用這種方式(成員初值設定語法)來作初值化 而 const 與參照 必須(只能)用成員初值設定語法來作初值化。 如果有多個資料成員需要如此初值化時 可用逗號隔開 測試提醒:當某個成員函式並不需要改變 資料成員的內容時,就將它設為 const,這樣無論物件是否為 const 都可以使用。 常見錯誤7.5:沒有提供 const 資料成員的初值,這是語法錯誤。

7.3 合成:將物件當作類別的成員 合成 composition 物件的建構 construction of objects 7.3 合成:將物件當作類別的成員 合成 composition Class has objects of other classes as members,即有其他物件為成員的類別 這種方式是最自然地再使用的作法。 例如員工資料中,有生日、聘用日期等,這些日期資料可以直接使用「日期」的物件。 物件的建構 construction of objects 成員物件按照宣告順序來建構(產生) 而不是照建構子的成員初值設定順序 Constructed before their enclosing class objects (host objects) ,小物件先建構(產生),這是物件的建構順序;而解構順序與此顛倒。

Date 10 month 1 day 1900 year 檢查日期是否正確的函式 1 // Fig. 7.6: date1.h,這個範例共有五個檔案 2 // Declaration of the Date class. 3 // Member functions defined in date1.cpp 4 #ifndef DATE1_H 5 #define DATE1_H 6 7 class Date { 8 public: 9 Date( int = 1, int = 1, int = 1900 ); // default constructor 10 void print() const; // print date in month/day/year format 11 ~Date(); // provided to confirm destruction order 12 private: 13 int month; // 1-12 14 int day; // 1-31 based on month 15 int year; // any year 16 17 // utility function to test proper day for month and year 18 int checkDay( int ); 19 }; 20 21 #endif 10 1900 day year Date 1 month 檢查日期是否正確的函式

當建構子被呼叫時會輸出一行文字 22 // Fig. 7.4: date1.cpp 23 // Member function definitions for Date class. 24 #include <iostream> 25 26 using std::cout; 27 using std::endl; 28 29 #include "date1.h" 30 31 // Constructor: Confirm proper value for month; 32 // call utility function checkDay to confirm proper 33 // value for day. 34 Date::Date( int mn, int dy, int yr ) 35 { 36 if ( mn > 0 && mn <= 12 ) // validate the month 37 month = mn; 38 else { 39 month = 1; 40 cout << "Month " << mn << " invalid. Set to month 1.\n"; 41 } 42 43 year = yr; // should validate yr 44 day = checkDay( dy ); // validate the day 45 46 cout << "Date object constructor for date "; 47 print(); // interesting: a print with no arguments 48 cout << endl; 49 } 50 當建構子被呼叫時會輸出一行文字

解構子被呼叫時、也會輸出一行文字 這是檢查日期是否合法的函式 51 // Print Date object in form month/day/year 52 void Date::print() const 53 { cout << month << '/' << day << '/' << year; } 54 55 // Destructor: provided to confirm destruction order 56 Date::~Date() 57 { 58 cout << "Date object destructor for date "; 59 print(); 60 cout << endl; 61 } 62 63 // Utility function to confirm proper day value 64 // based on month and year. 65 // Is the year 2000 a leap year? 66 int Date::checkDay( int testDay ) 67 { 68 static const int daysPerMonth[ 13 ] = 69 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 70 71 if ( testDay > 0 && testDay <= daysPerMonth[ month ] ) 72 return testDay; 73 74 if ( month == 2 && // February: Check for leap year 75 testDay == 29 && 76 ( year % 400 == 0 || 77 ( year % 4 == 0 && year % 100 != 0 ) ) ) 78 return testDay; 79 80 cout << "Day " << testDay << " invalid. Set to day 1.\n"; 81 82 return 1; // leave object in consistent state if bad value 83 } 解構子被呼叫時、也會輸出一行文字 這是檢查日期是否合法的函式

10 Employee firstname lastname Date birth Date hire 84 // Fig. 7.4: emply1.h 85 // Declaration of the Employee class. 86 // Member functions defined in emply1.cpp 87 #ifndef EMPLY1_H 88 #define EMPLY1_H 89 90 #include "date1.h" 91 92 class Employee { 93 public: 94 Employee( char *, char *, int, int, int, int, int, int ); 95 void print() const; 96 ~Employee(); // provided to confirm destruction order 97 private: 98 char firstName[ 25 ]; 99 char lastName[ 25 ]; 100 const Date birthDate; 101 const Date hireDate; 102 }; 103 104 #endif firstname lastname Employee 10 1900 day year Date birth 1 month Date hire 合成:有其他類別物件的成員,且是常數物件 物件建構的順序與宣告的順序相同。

要將兩個類別的 介面檔案都 include 進來 105 // Fig. 7.4: emply1.cpp 106 // Member function definitions for Employee class. 107 #include <iostream> 108 109 using std::cout; 110 using std::endl; 111 112 #include <cstring> 113 #include "emply1.h" 114 #include "date1.h" 115 116 Employee::Employee( char *fname, char *lname, 117 int bmonth, int bday, int byear, 118 int hmonth, int hday, int hyear ) 119 : birthDate( bmonth, bday, byear ), 120 hireDate( hmonth, hday, hyear ) 121 { 122 // copy fname into firstName and be sure that it fits 123 int length = strlen( fname ); 124 length = ( length < 25 ? length : 24 ); 125 strncpy( firstName, fname, length ); 126 firstName[ length ] = '\0'; 127 128 // copy lname into lastName and be sure that it fits 129 length = strlen( lname ); 130 length = ( length < 25 ? length : 24 ); 131 strncpy( lastName, lname, length ); 132 lastName[ length ] = '\0'; 133 134 cout << "Employee object constructor: " 135 << firstName << ' ' << lastName << endl; 136 } 要將兩個類別的 介面檔案都 include 進來 成員初值設定串列,兩個常數物件的初值設定 建構子被呼叫時會輸出一行文字

print 函式是 const 且當 Date 物件被建立或解構時會執行,因為它是 const 函式,所以可以輸出 const 物件。 137 138 void Employee::print() const 139 { 140 cout << lastName << ", " << firstName << "\nHired: "; 141 hireDate.print(); 142 cout << " Birth date: "; 143 birthDate.print(); 144 cout << endl; 145 } 146 147 // Destructor: provided to confirm destruction order 148 Employee::~Employee() 149 { 150 cout << "Employee object destructor: " 151 << lastName << ", " << firstName << endl; 152 } print 函式是 const 且當 Date 物件被建立或解構時會執行,因為它是 const 函式,所以可以輸出 const 物件。 Print 不需要引數,它隱含地連結到呼叫它的物件 解構子被呼叫時會輸出一行文字

Employee manager( "Bob", "Jones", birth, hire ) 153 // Fig. 7.4: fig07_04.cpp 154 // Demonstrating composition: an object with member objects. 155 #include <iostream> 156 157 using std::cout; 158 using std::endl; 159 160 #include "emply1.h" 161 162 int main() 163 { 164 Employee e( "Bob", "Jones", 7, 24, 1949, 3, 12, 1988 ); 165 166 cout << '\n'; 167 e.print(); 168 169 cout << "\nTest Date constructor with invalid values:\n"; 170 Date d( 14, 35, 1994 ); // invalid Date values 171 cout << endl; 172 return 0; 173 } Bob Jones firstname lastname Employee manager( "Bob", "Jones", birth, hire ) birth hire 只有 emply.h 需要被載入,該檔案就有載入 date.h. 10 1949 day year Date birth(7,24,1949) 24 7 month 10 1988 day year Date hire(3,12,1988) 12 3 month

Date object constructor for date 7/24/1949 Employee object constructor: Bob Jones Jones, Bob Hired: 3/12/1988 Birth date: 7/24/1949 Test Date constructor with invalid values: Month 14 invalid. Set to month 1. Day 35 invalid. Set to day 1. Date object constructor for date 1/1/1994 Date object destructor for date 1/1/1994 Employee object destructor: Jones, Bob Date object destructor for date 3/12/1988 Date object destructor for date 7/24/1949 注意內部的物件先被建立、且較慢被解構

7.3 合成:將物件當作類別的成員 當類別中有其他類別的成員時,一定要有預設的建構子。 7.3 合成:將物件當作類別的成員 當類別中有其他類別的成員時,一定要有預設的建構子。 兩個類別的 print() 函式都是 const,不需要改變資料成員的函式最好都設為 const。且這兩個 print 都沒有 augments。 firstName 與 lastName 兩個字串長度都是 25,比較浪費空間,而且若輸入長度超過 25 的字串時,會被切掉。後面使用動態記憶體管理時,可以改善這些情形。

7.4 夥伴(friend)函式和夥伴類別 friend 函式與 friend 類別 可以存取其他類別宣告在 private 或 protected 裡面的資料 friend 函式不是類別的成員函式,所以存取其 資料成員的方式不同。 定義在類別範圍之外 朋友關係的性質 Properties of friendship 朋友關係只能他人授權、而非自行取得 非對稱 (若 B 是 A 的朋友,A 未必就是 B 的朋友) 沒有遞移性(若 A 是 B 的朋友、 B 是 C 的朋友,A 未必是 C 的朋友)

7.4 夥伴函式和夥伴類別 friend 宣告 宣告夥伴(friend)函式 宣告夥伴(friend)類別 7.4 夥伴函式和夥伴類別 friend 宣告 宣告夥伴(friend)函式 將 friend 寫在要授權的類別中之函式原型的前面,如: friend int myFunction( int x ); 這一行應出現在要授權的類別裡面 宣告夥伴(friend)類別 若 Classtwo 要成為 Classone 的夥伴類別,必須在 Classone 中宣告 friend class ClassTwo;

這裡宣告 setX 是 Count 的 friend (可存取 private 資料). 1 // Fig. 7.5: fig07_05.cpp 2 // Friends can access private members of a class. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 // Modified Count class 9 class Count { 10 friend void setX( Count &, int ); // friend declaration 11 public: 12 Count() { x = 0; } // constructor 13 void print() const { cout << x << endl; } // output 14 private: 15 int x; // data member 16 }; 17 18 // Can modify private data of Count because 19 // setX is declared as a friend function of Count 20 void setX( Count &c, int val ) 21 { 22 c.x = val; // legal: setX is a friend of Count 23 } 24 25 int main() 26 { 27 Count counter; 28 29 cout << "counter.x after instantiation: "; 30 counter.print(); 這裡宣告 setX 是 Count 的 friend (可存取 private 資料). 可以改變 Count 的 private 變數,但存取方式與 Count 的成員函式不同 setX 被正常定義,且不是 Count 的成員函式Count.

雖然夥伴函式的原型出現在類別的定義裡,但它仍然不是類別的成員函式。 31 cout << "counter.x after call to setX friend function: "; 32 setX( counter, 8 ); // set x with a friend,一般涵數 33 counter.print(); 34 return 0; 35 } counter.x after instantiation: 0 counter.x after call to setX friend function: 8 private 資料已被改變。 軟體工程的觀點 7.9 雖然夥伴函式的原型出現在類別的定義裡,但它仍然不是類別的成員函式。

cannotSetX 不是 Count 的friend,不能存取其 private 資料 1 // Fig. 7.6: fig07_06.cpp 2 // Non-friend/non-member functions cannot access 3 // private data of a class. 4 #include <iostream> 5 6 using std::cout; 7 using std::endl; 8 9 // Modified Count class 10 class Count { 11 public: 12 Count() { x = 0; } // constructor 13 void print() const { cout << x << endl; } // output 14 private: 15 int x; // data member 16 }; 17 18 // Function tries to modify private data of Count, 19 // but cannot because it is not a friend of Count. 20 void cannotSetX( Count &c, int val ) 21 { 22 c.x = val; // ERROR: 'Count::x' is not accessible 23 } 24 25 int main() 26 { 27 Count counter; 28 29 cannotSetX( counter, 3 ); // cannotSetX is not a friend 30 return 0; 31 } cannotSetX 不是 Count 的friend,不能存取其 private 資料 cannotSetX 試著去修改private 變數 x

夥伴關係的宣告與 private、protected、public 的符號無關,因此夥伴關係的宣告可置於類別定義裡的任何位置。   Compiling... Fig07_06.cpp D:\books\2000\cpphtp3\examples\Ch07\Fig07_06\Fig07_06.cpp(22) : error C2248: 'x' : cannot access private member declared in class 'Count' D:\books\2000\cpphtp3\examples\Ch07\Fig07_06\ Fig07_06.cpp(15) : see declaration of 'x' Error executing cl.exe. test.exe - 1 error(s), 0 warning(s) 因此產生語法錯誤 -不可存取 private 資料 軟體工程的觀點 7.10 夥伴關係的宣告與 private、protected、public 的符號無關,因此夥伴關係的宣告可置於類別定義裡的任何位置。

7.5 this 指標的使用 this 指標 是指向物件本身的指標,允許物件存取自己的位置 不是物件本身的一部份,不會反應在 sizeof 中 呼叫物件的 non-static 成員函式時,隱含的第一個參數。 Implicitly reference member data and functions,呼叫本身的成員時沒寫物件名稱,就等於是加上 this-> 的意思。 this 指標的型態是根據該物件的型態與成員函式是否為 const 而定。

const Employee * const this 對於 Employee 的 non-const 成員函式, this 的型態 Employee * const 指向 Employee 物件的常數指標 在 Employee 的 const 成員函式,this 的型態為 const Employee * const 指向常數物件 Employee 的常數指標 Employee Employee * const this Employee const Employee * const this

7.5 this 指標的使用 使用 this 的範例 一連串的成員函式呼叫 成員函式中存取資料成員 x,有以下三種寫法 X, this->x, 或 (*this).x 一連串的成員函式呼叫 函式傳回一個指向相同物件的指標 { return *this; } 其他函式可以使用這個指標繼續作運算 沒有傳回這種指標參照的函必須最後被呼叫

1. Class definition 1.1 Function definition 1.2 Initialize object 1 // Fig. 7.7: fig07_07.cpp 2 // Using the this pointer to refer to object members. 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 class Test { 9 public: 10 Test( int = 0 ); // default constructor 11 void print() const; 12 private: 13 int x; 14 }; 15 16 Test::Test( int a ) { x = a; } // constructor 17 18 void Test::print() const // ( ) around *this required 19 { 20 cout << " x = " << x 21 << "\n this->x = " << this->x 22 << "\n(*this).x = " << ( *this ).x << endl; 23 } 24 25 int main() 26 { 27 Test testObject( 12 ); 28 29 testObject.print(); 30 31 return 0; 32 } Test this 12 x 直接輸出 x 使用 this 指標與箭頭(->)運算子來輸出 x 1. Class definition 1.1 Function definition 1.2 Initialize object 2. Function call 用點(.)運算子輸出 x。括號是必須的,因為點運算子的優先順序比 * 還高,沒寫括號的話就變成 *(this.x)的意思,造成語法錯誤。

  x = 12 this->x = 12 (*this).x = 12 三個方法輸出的結果都相同

7.5 this 指標的使用 連續呼叫成員函式的例子 成員函式 setHour, setMinute, 與 setSecond 都傳回 *this (指向物件) 對物件 t來說,考慮下列呼叫 t.setHour(1).setMinute(2).setSecond(3); 先執行 t.setHour(1), 傳回 *this (指向物件)成為 t.setMinute(2).setSecond(3); 再執行 t.setMinute(2), 傳回 *this 成為 t.setSecond(3); 最後執行 t.setSecond(3), 傳回 *this 成為 t; 不會再產生其他結果

注意函式傳回的資料型態是 Time & - 指向 Time 物件的參照. 1 // Fig. 7.8: time6.h 2 // Cascading member function calls. 3 4 // Declaration of class Time. 5 // Member functions defined in time6.cpp 6 #ifndef TIME6_H 7 #define TIME6_H 8 9 class Time { 10 public: 11 Time( int = 0, int = 0, int = 0 ); // default constructor 12 13 // set functions 14 Time &setTime( int, int, int ); // set hour, minute, second 15 Time &setHour( int ); // set hour 16 Time &setMinute( int ); // set minute 17 Time &setSecond( int ); // set second 18 19 // get functions (normally declared const) 20 int getHour() const; // return hour 21 int getMinute() const; // return minute 22 int getSecond() const; // return second 23 24 // print functions (normally declared const) 25 void printMilitary() const; // print military time 26 void printStandard() const; // print standard time 27 private: 28 int hour; // 0 - 23 29 int minute; // 0 - 59 30 int second; // 0 - 59 31 }; 32 33 #endif 注意函式傳回的資料型態是 Time & - 指向 Time 物件的參照.

傳回 *this 因此可以作連續的函式呼叫 34 // Fig. 7.8: time.cpp 35 // Member function definitions for Time class. 36 #include <iostream> 37 38 using std::cout; 39 40 #include "time6.h" 41 42 // Constructor function to initialize private data. 43 // Calls member function setTime to set variables. 44 // Default values are 0 (see class definition). 45 Time::Time( int hr, int min, int sec ) 46 { setTime( hr, min, sec ); } 47 48 // Set the values of hour, minute, and second. 49 Time &Time::setTime( int h, int m, int s ) 50 { 51 setHour( h ); 52 setMinute( m ); 53 setSecond( s ); 54 return *this; // enables cascading 55 } 56 57 // Set the hour value 58 Time &Time::setHour( int h ) 59 { 60 hour = ( h >= 0 && h < 24 ) ? h : 0; 61 62 return *this; // enables cascading 63 } 64 傳回 *this 因此可以作連續的函式呼叫

傳回 *this 因此可以作連續的函式呼叫 65 // Set the minute value 66 Time &Time::setMinute( int m ) 67 { 68 minute = ( m >= 0 && m < 60 ) ? m : 0; 69 70 return *this; // enables cascading 71 } 72 73 // Set the second value 74 Time &Time::setSecond( int s ) 75 { 76 second = ( s >= 0 && s < 60 ) ? s : 0; 77 78 return *this; // enables cascading 79 } 80 81 // Get the hour value 82 int Time::getHour() const { return hour; } 83 84 // Get the minute value 85 int Time::getMinute() const { return minute; } 86 87 // Get the second value 88 int Time::getSecond() const { return second; } 89 90 // Display military format time: HH:MM 91 void Time::printMilitary() const 92 { 93 cout << ( hour < 10 ? "0" : "" ) << hour << ":" 94 << ( minute < 10 ? "0" : "" ) << minute; 傳回 *this 因此可以作連續的函式呼叫

printStandard 沒有傳回指向物件的參照 95 } 96 97 // Display standard format time: HH:MM:SS AM (or PM) 98 void Time::printStandard() const 99 { 100 cout << ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ) 101 << ":" << ( minute < 10 ? "0" : "" ) << minute 102 << ":" << ( second < 10 ? "0" : "" ) << second 103 << ( hour < 12 ? " AM" : " PM" ); 104 } printStandard 沒有傳回指向物件的參照 105 // Fig. 7.8: fig07_08.cpp 106 // Cascading member function calls together 107 // with the this pointer 108 #include <iostream> 109 110 using std::cout; 111 using std::endl; 112 113 #include "time6.h" 114 115 int main() 116 { 117 Time t; 118 119 t.setHour( 18 ).setMinute( 30 ).setSecond( 22 ); 120 cout << "Military time: "; 121 t.printMilitary(); 122 cout << "\nStandard time: "; 123 t.printStandard(); 124 125 cout << "\n\nNew standard time: "; 126 t.setTime( 20, 20, 20 ).printStandard(); 注意這裡作連續函式呼叫 連續函式呼叫, printStandard 必須在 setTime 之後再呼叫,因為 printStandard 並沒有傳回指向物件的參照。 t.printStandard().setTime(); 若如此呼叫會造成語法錯誤。

New standard time: 8:20:20 PM 127 cout << endl; 128 129 return 0; 130 } Military time: 18:30 Standard time: 6:30:22 PM   New standard time: 8:20:20 PM

7.6 使用 new 和 delete 運算子的動態記憶體配置 作動態記憶體配置之用,C 語言中也有類似功能的函式呼叫- malloc 與 free,C++ 的 new 與 delete 比較好。 new 建立適當大小的物件,呼叫其建構子並傳回正確型態的指標 delete 解構物件並釋放記憶體空間 delete 的範例 delete typeNamePtr; 呼叫 TypeName 物件的解構子並釋放記憶體 delete [] arrayPtr; 用來動態地殺掉一個陣列

7.6 使用 new 和 delete 運算子的動態記憶體配置 TypeName *typeNamePtr; 建立一個指向 TypeName 物件的指標 typeNamePtr = new TypeName; new 建立 TypeName 物件,傳回指標(存到 typeNamePtr 裡面) Time *timePtr; timePtr = new Time; 對物件作初值化 double *ptr = new double( 3.14159 ); Time *timePtr = new Time( 12, 0, 0 ); c.f. : double v = 3.14159; ptr = &v; timePtr hour minute second Time( 0, 0, 0 ); timePtr 12 hour minute second Time( 12, 0, 0 ); ptr 3.14259

7.6 使用 new 和 delete 運算子的動態記憶體配置 對物件作初值化 建立一10個元素的 int 陣列並設定給 arrayPtr int *gradesArray = new int[ 10 ]; 用 new 建立物件時,會自動呼叫建構子;而 delete 會自動呼叫解構子。 gradesArray

7.6 使用 new 和 delete 運算子的動態記憶體配置 常見錯誤7.8: 將 new-和-delete-型式的動態記憶體配置與 malloc-和-free-型式的動態記憶體配置相混雜是邏輯錯誤,也就是用 malloc 配置的空間不可用 delete來釋放;而用 new 建立的物件不可用 free 來刪除。 常見錯誤7.9: 對陣列使用 delete 來取代 delete [] 會引起執行時的邏輯錯誤;謹記:動態建立的陣列空間必須用delete [] 運算子刪除,而動態建立的個別元素就用 delete 運算子刪除。 好的習慣 7.3: C++ 都可以使用 C 的敘述式,所以在 C++ 中也可以使用 malloc 與 free。但最好單單使用 new 與 delete 不要用 malloc 與 free。

7.7 static 類別成員 static 類別成員 一般說來,每個物件會記錄一份資料變數的內容 例如:要記錄所建構的物件個數時,使用 static 類別成員比較有效率、又可節省空間 只需改變 static 變數內容,而不需改每個物件內容 可視為類別範圍的全域變數 只能被同一個類別的物件存取 用檔案範圍的方式作初值化 就算沒有任何物件被宣告,此種成員也存在、可使用 變數與函式都可被宣告為 static 可設成 public, private 或 protected

7.7 static 類別成員 static 變數 可以透過類別名稱來存取 static 成員的值。 public static 變數 class C { int x; static int s; }; C c1, c2; C::s x static 變數 可以透過類別名稱來存取 static 成員的值。 public static 變數 可以用範圍解析運算子(::)來存取 Employee::count private static 變數 當沒有該類別的物件存在時,必須透過 public static 成員函式來存取。 用類別名稱加範圍解析運算子呼叫 public static 成員函式 Employee::getCount() 一般成員函式或資料成員只能透過物件來呼叫,但是 static 資料成員或成員函式可以透過類別名稱來使用。

7.7 static 類別成員 static 函式 static 成員函式不可存取非 static 的資料或呼叫非 static 的函式。 static 函式沒有 this 指標,他們的存在與物件無關。

Employee object Employee::count firstName lastName static 成員函式與變數宣告 1 // Fig. 7.9: employ1.h 2 // An employee class 3 #ifndef EMPLOY1_H 4 #define EMPLOY1_H 5 6 class Employee { 7 public: 8 Employee( const char*, const char* ); // constructor 9 ~Employee(); // destructor 10 const char *getFirstName() const; // return first name 11 const char *getLastName() const; // return last name 12 13 // static member function 14 static int getCount(); // return # objects instantiated 15 16 private: 17 char *firstName; 18 char *lastName; 19 20 // static data member 21 static int count; // number of objects instantiated 22 }; 23 24 #endif Employee object Employee::count firstName lastName static 成員函式與變數宣告

static 資料成員 count 和成員函式 getCount( ) 函式需用檔案範圍作初值化 25 // Fig. 7.9: employ1.cpp 26 // Member function definitions for class Employee 27 #include <iostream> 28 29 using std::cout; 30 using std::endl; 31 32 #include <cstring> 33 #include <cassert> 34 #include "employ1.h" 35 36 // Initialize the static data member 37 int Employee::count = 0; 38 39 // Define the static member function that 40 // returns the number of employee objects instantiated. 41 int Employee::getCount() { return count; } 42 43 // Constructor dynamically allocates space for the 44 // first and last name and uses strcpy to copy 45 // the first and last names into the object 46 Employee::Employee( const char *first, const char *last ) 47 { 48 firstName = new char[ strlen( first ) + 1 ]; 49 assert( firstName != 0 ); // ensure memory allocated 50 strcpy( firstName, first ); 51 52 lastName = new char[ strlen( last ) + 1 ]; 53 assert( lastName != 0 ); // ensure memory allocated 54 strcpy( lastName, last ); 55 56 ++count; // increment static count of employees static 資料成員 count 和成員函式 getCount( ) 函式需用檔案範圍作初值化 注意 assert 用來測試 firstName != 0 這個式子若不成立(等於記憶體配置失敗),就輸出錯誤訊息,並呼叫 abort() 來作異常結束。 當建構子或解構子被呼叫時就改變 static 資料成員 count 的內容

當建構子或解構子被呼叫時就改變 static 資料成員 count 的內容 57 cout << "Employee constructor for " << firstName 58 << ' ' << lastName << " called." << endl; 59 } 60 61 // Destructor deallocates dynamically allocated memory 62 Employee::~Employee() 63 { 64 cout << "~Employee() called for " << firstName 65 << ' ' << lastName << endl; 66 delete [] firstName; // recapture memory 67 delete [] lastName; // recapture memory 68 --count; // decrement static count of employees 69 } 70 71 // Return first name of employee 72 const char *Employee::getFirstName() const 73 { 74 // Const before return type prevents client from modifying 75 // private data. Client should copy returned string before 76 // destructor deletes storage to prevent undefined pointer. 77 return firstName; 78 } 79 80 // Return last name of employee 81 const char *Employee::getLastName() const 82 { 83 // Const before return type prevents client from modifying 84 // private data. Client should copy returned string before 85 // destructor deletes storage to prevent undefined pointer. 86 return lastName; 87 } 當建構子或解構子被呼叫時就改變 static 資料成員 count 的內容

Employee::count Employee *e2Ptr firstName lastName Employee *e2Ptr 88 // Fig. 7.9: fig07_09.cpp 89 // Driver to test the employee class 90 #include <iostream> 91 92 using std::cout; 93 using std::endl; 94 95 #include "employ1.h" 96 97 int main() 98 { 99 cout << "Number of employees before instantiation is " 100 << Employee::getCount() << endl; // use class name 101 102 Employee *e1Ptr = new Employee( "Susan", "Baker" ); 103 Employee *e2Ptr = new Employee( "Robert", "Jones" ); 104 105 cout << "Number of employees after instantiation is " 106 << e1Ptr->getCount(); 107 108 cout << "\n\nEmployee 1: " 109 << e1Ptr->getFirstName() 110 << " " << e1Ptr->getLastName() 111 << "\nEmployee 2: " 112 << e2Ptr->getFirstName() 113 << " " << e2Ptr->getLastName() << "\n\n"; 114 115 delete e1Ptr; // recapture memory 116 e1Ptr = 0; 117 delete e2Ptr; // recapture memory 118 e2Ptr = 0; 沒有 Employee 物件存在時 getCount 必須用類別名稱加(::)來存取。 count 增加 1, 因為 new 使建構子被呼叫 Employee *e2Ptr Robert firstName Jones lastName Employee *e2Ptr Susan firstName Baker lastName Employee *e1Ptr Employee *e1Ptr 2 1 Employee::count

Number of employees before instantiation is 0 119 120 cout << "Number of employees after deletion is " 121 << Employee::getCount() << endl; 122 123 return 0; 124 } 最後 count 變成 0 Number of employees before instantiation is 0 Employee constructor for Susan Baker called. Employee constructor for Robert Jones called. Number of employees after instantiation is 2   Employee 1: Susan Baker Employee 2: Robert Jones ~Employee() called for Susan Baker ~Employee() called for Robert Jones Number of employees after deletion is 0

7.7 static 類別成員 前面範例中有用到 new 與 delete。 前面範例中 getFirstName() 與 getLastName() 的傳回值資料型態是 const char,如果沒有加 const 會如何呢? 與 6.15 傳回值資料型態為 reference 的變數類似。 這個範例中的 firstName 與 lastName 的用法是動態的作法,能改善 Figure07_04 的問題。 static 變數作初值化時,不加 static 這個字。 用到 assert 需要 #include <cassert> 才行。 常見錯誤7.11: 在 static 成員函式中使用 this 指標. 常見錯誤7.12: 將 static 成員函式宣告成 const.

7.8 資料抽象化與資訊隱藏 資訊隱藏 類別將實作細節對客戶端隱藏起來 範例:堆疊資料結構 7.8 資料抽象化與資訊隱藏 資訊隱藏 類別將實作細節對客戶端隱藏起來 範例:堆疊資料結構 資料從上面加入(push)、也從上面移除(pop) 後進先出 Last-in, first-out (LIFO) 的資料結構 客戶端不需注意堆疊如何被實作,只需關心它是有提供堆疊功能的資料結構即可。

7.8 資料抽象化與資訊隱藏 抽象資料型態 Abstract data types (ADTs) C++ 是可擴展的語言 7.8 資料抽象化與資訊隱藏 抽象資料型態 Abstract data types (ADTs) 包含:資料表示方式與運算 用來模擬真實世界的物件,類別的功用與實作方式無關 int, float 模擬數字的概念。 C++ 是可擴展的語言 標準的資料型態不能改變,但可建立新的資料型態。 軟體工程觀察7.15: 程式設計者可透過類別的機制來建立新的型態,這些新的型態可以與內建的型態同樣方便使用,因此C++是可擴展的程式語言,雖然C++容易擴展(建立新的型態),但它的基本觀念沒有改變。

7.8.1 範例:陣列抽象資料型態 C++ 所提供的陣列就是常數指標加上它的空間。 程式設計者可以自己作 ADT 陣列 可包括 7.8.1 範例:陣列抽象資料型態 C++ 所提供的陣列就是常數指標加上它的空間。 程式設計者可以自己作 ADT 陣列 可包括 Subscript range checking,檢查存取範圍 An arbitrary range of subscripts instead of having to start with 0,駐標不一定要由 0 開始 Array assignment,將一個陣列存到另一個陣列中 Array comparison,比較兩陣列是否相同 Array input/output,輸入/出陣列 Arrays that know their sizes ,知道陣列的大小 Arrays that expand dynamically to accommodate more elements ,可動態地容納更多元素 第八章會建 array 的類別

7.8.2 範例:字串抽象型態 C++ 的字串 C++ 本身沒有提供字串的資料型態,主要應該是執行效率的考量,因為 C++ 很重視執行效率,所以沒有建很多種基本的資料型態。 C++ 就是提供可以建立字串之抽象資料型態的能力,讓人可以建自己的字串,加入標準程式庫中。 第八章會有我們自己建的字串ADT string 類別有在 ANSI/ISO 標準中 (Chapter 19)

7.8.3 範例:佇列抽象型態 佇列 Queue 佇列的抽象資料型態 就像排隊(超市、加油站、公車站、投票、餐廳、…) 7.8.3 範例:佇列抽象型態 佇列 Queue 就像排隊(超市、加油站、公車站、投票、餐廳、…) FIFO — First in, first out(先進先出) 存入佇列 enqueue 每次從後面加一個資料到佇列中 取出佇列 dequeue 每次從佇列前面取出一個資料 實作方式對客戶端隱藏 佇列的抽象資料型態 客戶端不直接操作資料結構 只有佇列的成員函式才存取內部資料 第十五章會討論佇列的資料結構

7.9 容器類別與迭代子(iterators) 容器(container)類別,或稱集合(collection)類別 設計來存放物作集合的類別 提供像:插入、刪除、搜尋、排序、測試某元素是否屬在該容器的測試函式 範例: 陣列、堆疊、佇列、樹、鏈結串列 迭代子物件(簡稱迭代子 iterators) 用來傳回容器中下一個項目的物件(或對下個項目執行一些動作) 每個容器可以有好幾個迭代子 就像很多人一起讀一本書時,書中會夾好幾張書籤一樣 每個迭代子都有存有自己「位置」的資訊 二十章會有更多討論

7.10 代理類別 proxy class 代理類別 先置類別宣告 Forward class declaration 用來將類別實作細節完全隱藏起來 只讓客戶端看到 interface 的資料,看不到類別中有哪些 private 資料。 使客戶端使用類別提供的服務,而無法存取類別實作。 先置類別宣告 Forward class declaration 當類別定義中只用指標指到另一個類別時 避免將另一個類別的標頭檔載入 在使用到這個類別之前就先宣告 格式: class ClassToLoad;

Implementation 中要隱藏的 private 資料(value) 1 // Fig. 7.10: implementation.h 2 // Header file for class Implementation 3 4 class Implementation { 5 public: 6 Implementation( int v ) { value = v; } 7 void setValue( int v ) { value = v; } 8 int getValue() const { return value; } 9 10 private: 11 int value; 12 }; 13 // Fig. 7.10: interface.h 14 // Header file for interface.cpp 15 class Implementation; // forward class declaration 16 17 class Interface { 18 public: 19 Interface( int ); 20 void setValue( int ); // same public interface as 21 int getValue() const; // class Implementation 22 ~Interface(); 23 private: 24 Implementation *ptr; // requires previous 25 // forward declaration 26 }; Implementation 中要隱藏的 private 資料(value) Forward class declaration先置類別宣告. 代理類別 Interface 的公開介面幾乎與 Implementation 完全一樣 只用指標指到類別 Implementation. 這樣允許我們將實作細節隱藏起來。

27 // Fig. 7.10: interface.cpp 28 // Definition of class Interface 29 #include "interface.h" 30 #include "implementation.h" 31 32 Interface::Interface( int v ) 33 : ptr ( new Implementation( v ) ) { } 34 35 // call Implementation's setValue function 36 void Interface::setValue( int v ) { ptr->setValue( v ); } 37 38 // call Implementation's getValue function 39 int Interface::getValue() const { return ptr->getValue(); } 40 41 Interface::~Interface() { delete ptr; } 42 // Fig. 7.10: fig07_10.cpp 43 // Hiding a class’s private data with a proxy class. 44 #include <iostream> 45 46 using std::cout; 47 using std::endl; 48 49 #include "interface.h" 50 51 int main() 52 { 53 Interface i( 5 ); 54 55 cout << "Interface contains: " << i.getValue() 56 << " before setValue" << endl; 57 i.setValue( 10 ); 58 cout << "Interface contains: " << i.getValue() 59 << " after setValue" << endl; 60 return 0; 61 } 在實作的檔案 interface.cpp 中包含類別 Interface 所用到的所有成員函式,它也是唯一需要載入標頭檔 implementation.h 的部份 將 interface.cpp 編譯好,在客戶端就只要載入標頭檔 interface.h 即可,客戶端無法看到代理類別與被隱藏的類別間如何互動。 只有標頭檔 Interface.h 完全沒有提到 Implementation,客戶端完全看不到 Implementation 的 private 有什麼資料。

Interface contains: 5 before setVal Interface contains: 10 after setVal