Microsoft Word - ACL0204-ch11-ok.doc

Similar documents
運算子多載 Operator Overloading

新版 明解C++入門編

踏出C++的第一步

untitled

c_cpp

Microsoft PowerPoint - Class5.pptx

第二章 簡介類別

Microsoft PowerPoint - string_kruse [兼容模式]

陣列與鏈結串列 Array and Linked List

Microsoft PowerPoint - 04-array_pointer.ppt

資料結構之C語言重點複習

投影片 1

FY.DOC

第3章.doc

2013 C 1 # include <stdio.h> 2 int main ( void ) 3 { 4 int cases, a, b, i; 5 scanf ("%d", & cases ); 6 for (i = 0;i < cases ;i ++) 7 { 8 scanf ("%d %d

Fun Time (1) What happens in memory? 1 i n t i ; 2 s h o r t j ; 3 double k ; 4 char c = a ; 5 i = 3; j = 2; 6 k = i j ; H.-T. Lin (NTU CSIE) Referenc

C++ 程式設計

C/C++基礎程式設計班

Chapter 9: Objects and Classes

運算子多載 Operator Overloading

Microsoft Word - ch04三校.doc

0 0 = 1 0 = 0 1 = = 1 1 = 0 0 = 1

C 1 # include <stdio.h> 2 int main ( void ) { 4 int cases, i; 5 long long a, b; 6 scanf ("%d", & cases ); 7 for (i = 0;i < cases ;i ++) 8 { 9

Microsoft PowerPoint - STU_C_Lang_CH13.ppt

1: public class MyOutputStream implements AutoCloseable { 3: public void close() throws IOException { 4: throw new IOException(); 5: } 6:

Microsoft Word - chap10.doc

ebook39-5

PowerPoint Presentation

PowerPoint Presentation

Strings

Strings

Chapter 3 Camera Raw Step negative clarity +25 ] P / Step 4 0 ( 下一頁 ) Camera Raw Chapter 3 089

C/C++语言 - C/C++数据

untitled

Microsoft PowerPoint - Class4.pptx

Microsoft Word - 把时间当作朋友(2011第3版)3.0.b.07.doc

untitled

C C C The Most Beautiful Language and Most Dangerous Language in the Programming World! C 2 C C C 4 C Project 30 C Project 3 60 Project 40

CC213

Microsoft Word - ACL chapter02-5ed.docx

全国计算机技术与软件专业技术资格(水平)考试

Microsoft Word - 01.DOC

提问袁小兵:

Microsoft Word - 物件導向編程精要.doc

The golden pins of the PCI card can be oxidized after months or years

概述

主程式 : public class Main3Activity extends AppCompatActivity { ListView listview; // 先整理資料來源,listitem.xml 需要傳入三種資料 : 圖片 狗狗名字 狗狗生日 // 狗狗圖片 int[] pic =new

The Embedded computing platform

untitled

Microsoft PowerPoint - Bronson-v3-ch07.ppt [相容模式]

840 提示 Excel - Excel -- Excel (=) Excel ch0.xlsx H5 =D5+E5+F5+G5 (=) = - Excel 00

Microsoft Word - part doc

任務二 : 產生 20 個有炸彈的磚塊, 放在隨機的位置編輯 Block 類別的程式碼 import greenfoot.; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) Write a description of class

單步除錯 (1/10) 打開 Android Studio, 點選 Start a new Android Studio project 建立專案 Application name 輸入 BMI 點下 Next 2 P a g e

<4D F736F F F696E74202D FB5F8B3A5A142B8EAAEC6B6C7BBBCA142BB50C0C9AED7BEDEA7402E >

Microsoft Word - 投影片ch11

Factory Methods

文档 3

用手機直接傳值不透過網頁連接, 來當作搖控器控制家電 ( 電視遙控器 ) 按下按鍵發送同時會回傳值來確定是否有送出 問題 :1. 應該是使用了太多 thread 導致在傳值上有問題 2. 一次按很多次按鈕沒辦法即時反應

Scott Effective C++ C++ C++ Roger Orr OR/2 ISO C++ Effective Modern C++ C++ C++ Scoot 42 Bart Vandewoestyne C++ C++ Scott Effective Modern C++ Damien

<4D F736F F F696E74202D20332D322E432B2BC3E6CFF2B6D4CFF3B3CCD0F2C9E8BCC6A1AAD6D8D4D8A1A2BCCCB3D0A1A2B6E0CCACBACDBEDBBACF2E707074>

chp6.ppt

新・明解C言語入門編『索引』

4.1 AMI MQSeries API AMI MQI AMI / / AMI JavaC C++ AMI / AMI AMI - / /

BOOL EnumWindows(WNDENUMPROC lparam); lpenumfunc, LPARAM (Native Interface) PowerBuilder PowerBuilder PBNI 2

无类继承.key

第 1 頁 96 年 指 考 第 壹 部 分 : 選 擇 題 ( 佔 55 分 ) 一 單 選 題 (34 分 ) 說 明 : 第 1 至 第 17 題, 每 題 選 出 一 個 最 適 當 的 選 項, 劃 記 在 答 案 卡 之 選 擇 題 答 案 區 每 題 答 對 得 2 分, 答 錯 或

C 語言—陣列及字串

Microsoft PowerPoint - 4. 数组和字符串Arrays and Strings.ppt [兼容模式]

<4D F736F F D B0EAA5C1A470BEC7A4CEB0EAA5C1A4A4BEC7B8C9B1CFB1D0BEC7B9EAAC49A4E8AED7>

程序设计语言及基础

Python a p p l e b e a r c Fruit Animal a p p l e b e a r c 2-2

書面

Strings

一、

<ADB6ADB1C25EA8FAA6DB2D4D56432E706466>

威 福 髮 藝 店 桃 園 市 蘆 竹 區 中 山 里 福 祿 一 街 48 號 地 下 一 樓 50,000 獨 資 李 依 純 105/04/06 府 經 登 字 第 號 宏 品 餐 飲 桃 園 市 桃 園 區 信 光 里 民

东 藏 记

Excel VBA Excel Visual Basic for Application

untitled

untitled

Transcription:

第十一章 類別和動態記憶體配置 這章主要討論如何在類別中使用 new 和 delete 及如何處理動態記憶體所產生的一些問題, 主題似乎很少, 但這些主題會影響建構函數及解構函數的設計以及運算子的多型 先看一個特別的例子 若要產生一個類別, 其成員表示姓名, 最簡單的方法是用字元陣列儲存 但其缺點是字元陣列的長度該設多少呢? 若為 14 個字元的陣列, 遇到 Barthlomew Smeadsbury-Crafthovingham 時就不夠了, 為安全起見, 改用 40 個字元的陣列, 但若產生 2000 個此種物件的陣列, 因很多字元陣列只用去部分記憶體空間, 所以浪費了很多空間 還好我們可以利用 new 運算子來動態地配置正確的記憶體空間 動態記憶體配置和類別 對記憶體配置,C++ 採用的策略是 : 在程式所需要的記憶體大小, 是在執行期決定而非編譯期 C++ 使用 new 和 delete 來取得記憶體的動態控制權, 很不幸的, 在類別中使用這些運算子會產生一些新的程式設計問題, 接下來就來討論這些問題 靜態的類別成員及其範例 在看 new 和 delete 的範例前, 先介紹 String 類別中新的儲存類型 : 靜態類別成員 這類別會存一個指向字串的指標和一個表示字串長度的值 為了能從 String 類別來看 new,delete 及靜態類別成員是如何運作, 所以在建構函數和解構函數被呼叫時將顯示一些訊息 範例程式 11-1 是類別宣告, 取名為 strng1.h 是為了避免和標準的函數檔 string.h 產生衝突

C++ Primer Plus 5/e 中文精華版 範例程式 11-1 strng1.h // strng1.h -- string class definition #ifndef _STRNG1_H_ #define _STRNG1_H_ class String private: char *str; // pointer to string int len; // length of string static int num_strings; // number of objects public: String(const char * s); // constructor String(); // default constructor ~String(); // destructor // friend function friend ostream & operator<<(ostream & os, const String & st); ; #endif 此宣告中要注意兩件事 首先 str 的資料型態是用 char 指標而非 char 陣列, 代表在類別宣告時並未配置空間予字串, 而須在建構函數中利用 new 為字串配置空間, 如此才能避免受限於事先定義的字串大小 第二, 定義成員 num_strings 為靜態的儲存類別 此類別有一特殊性質, 不管產生多少個物件, 程式只產生一個靜態類別的變數, 意思是靜態成員為該類別的所有物件所共用, 好比全家人共用一個電話號碼 假設產生了 10 個 String 物件, 分別有 10 個 str 及 len, 但只有一個共用的 num_strings, 如圖 11-1 所示 num_strings 記錄此程式產生的物件個數 範例程式 11-2 為類別 String 的實作 範例程式 11-2 string1.cpp // strngs1.cpp -- String class methods #include <iostream> using namespace std; #include <cstring> #include "strng1.h" 11-2

第 11 章 類別和動態記憶體配置 // initializing static class member int String::num_strings = 0; // class methods String::String(const char * s) // construct String from C string len = strlen(s); // set size str = new char[len + 1]; // dynamic allocation memory strcpy(str, s); // initialize pointer num_strings++; // 累計物件個數 cout << num_strings << ": \"" << str << "\" object created\n"; String::String() // default constructor static const char * s = "C++ "; len = strlen(s); str = new char[len + 1]; strcpy(str, s); // default string num_strings++; cout << num_strings << ": \"" << str << "\" object created\n"; String::~String() // necessary destructor cout << "\"" << str << "\" object deleted, "; --num_strings; // required cout << num_strings << " left\n"; delete [] str; ostream & operator<<(ostream & os, const String & st) os << st.str; return os; 11-3

C++ Primer Plus 5/e 中文精華版 範例程式 11-2 中有一敘述為 : int string::num_strings = 0; 此敘述是將 num_strings 初始化為 0 此敘述不可以放在類別宣告的檔案中, 這是因為類別宣告放在標頭檔中, 程式可能有數個檔案皆引入此檔, 會造成多重初始化敘述, 而產生錯誤 圖 11-1 靜態成員資料 在建構函數中有運算式 num_strings++, 確保程式每產生一個新物件, 共用的變數 num_strings 就能遞增 1, 以記錄 String 類別的物件數, 同樣在解構函數亦含運算式 --num_strings, 減掉刪除的物件 11-4

第 11 章 類別和動態記憶體配置 下面為第一個建構函數, 是一個指標, 建構函數需為其配置記憶體, 初始化物件時, 可傳字串給建構函數 : String::String() // default constructor static const char * s = "C++ "; len = strlen(s); str = new char[len + 1]; strcpy(str, s); // default string num_strings++; cout << num_strings << ": \"" << str << "\" object created\n"; 呼叫解構函數時會顯示文字, 這部分只是為了觀察用, 但不是必要的敘述 str 所指的位置是由 new 配置, 當 String 物件結束, 會自動清除 str 指標, 但其所指的記憶體並不會自動清除, 所以必需在解構函數中呼叫 delete, 以確保物件結束時能釋放 new 所配置的記憶體 String::~String() cout << "\"" << str << "\" object deleted, "; --num_strings // required cout << num_strings << " left\n"; delete [] str; // required 範例程式 11-3 說明 String 的建構函數和解構函數如何運作, 請記得將範例程式 11-2 和 11-3 一起編譯 範例程式 11-3 vegnews.cpp // vegnews.cpp -- using new and delete with classes // compile with strngs.cpp #include <iostream> using namespace std; #include "strng1.h" 11-5

C++ Primer Plus 5/e 中文精華版 String sports("spinach Leaves Bowl for Dollars"); // sports an external object void callme1(void); // creates local object String * callme2(void); // creates dynamic object int main(void) cout << "Top of main()\n"; String headlines[2] = // 區域陣列物件變數 String("Celery Stalks at Midnight"), String("Lettuce Prey") ; cout << headlines[0] << "\n"; cout << headlines[1] << "\n"; callme1(); cout << "Middle of main()\n"; String *pr = callme2(); // set pointer to object cout << sports << "\n"; cout << *pr << "\n"; delete pr; // 回收物件 cout << "End of main()\n"; system("pause"); return 0; void callme1(void) cout << "Top of callme1()\n"; String grub; // local object cout << grub << "\n"; cout << "End of callme1()\n"; String * callme2(void) cout << "Top of callme2()\n"; String *pveg = new String("Cabbage Heads Home"); // dynamic object uses constructor cout << *pveg << "\n"; cout << "End of callme2()\n"; return pveg; // pveg expires, object lives 11-6

第 11 章 類別和動態記憶體配置 輸出結果如下 : 1: "Spinach Leaves Bowl for Dollars" object created Top of main() 2: "Celery Stalks at Midnight" object created 3: "Lettuce Prey" object created Celery Stalks at Midnight Lettuce Prey Top of callme1() 4: "C++ " object created C++ End of callme1() "C++ " object deleted, 3 left Middle of main() Top of callme2() 4: "Cabbage Heads Home" object created Cabbage Heads Home End of callme2() Spinach Leaves Bowl for Dollars Cabbage Heads Home "Cabbage Heads Home" object deleted, 3 left End of main() "Lettuce Prey" object deleted, 2 left "Celery Stalks at Midnight" object deleted, 1 left "Spinach Leaves Bowl for Dollars" object deleted, 0 left 程式摘要 為確實了解程式的流程, 我們將逐步解釋此程式 物件 sports 為外部變數, 所以會在 main() 執行前產生 接下來產生兩個元素的 headlines 物件陣列, 此時程式呼叫建構函數兩次, 每一次初始化陣列的一個元素, 且每個元素都是一個類別物件, 所以敘述 cout << headlines[0] << "\n"; cout << headlines[1] << "\n"; 會呼叫夥伴函數 operator<<() 11-7

C++ Primer Plus 5/e 中文精華版 接著程式呼叫函數 callme1(), 此函數利用預設建構函數產生一個區域物件 grub, 預設建構函數將成員 str 初始化成 "C++ " 當 callme1() 結束, 就清除此物件, 故有如以下的輸出結果 : Top of callme1() 4: "C++ " object created C++ End of callme1() "C++ " object deleted, 3 left 除了印出個別訊息, 解構函數亦清除 "C++ " 所佔的空間 接著進入程式的重要部份, 程式呼叫 callme2(), 此函數用 new 產生和初始化一個 String 物件 : String *pveg = new String("Cabbage Heads Home"); 函數將新物件的位址指定給 pveg 指標 因為在 new String 時有字串引數, 所以程式會呼叫對應的建構函數來初始化物件 pveg 為一個物件指標, 所以 *pveg 是一個物件, 其行為和宣告的物件相同 : cout << *pveg << "\n"; 因此會輸出 Cabbage Heads Home 函數結束時, 會自動清除其變數所佔的空間, 即指標 pveg 所存的空間, 但 callme2() 並未釋還 pveg 所以其所指的記憶體空間, 即 Cabbage Heads Home 物件仍然存在 因 pveg 之值傳給呼叫程式, 而將值指定給指標 pr 變數 大體來說,pveg 先指向 String 物件, 然後在清除 pveg 的同時將 pr 指向此 String 物件, 因此, 程式可再利用 pr 存取此動態配置的物件 在顯示 sports 物件後, 即輸出此結果 : 11-8

第 11 章 類別和動態記憶體配置 Top of callme2() 4: "Cabbage Heads Home" object created Cabbage Heads Home uses pveg pointer in callme2() End of callme2() Spinach Leaves Bowl for Dollars Cabbage Heads Home uses pr pointer in main() 程式用 delete 將 new 產生的物件刪除 : delete pr; 此乃釋放 pr 所指的記憶體, 並非 pr 本身, 也就是刪除物件, 因此會啟動解構函數刪除 Cabbage Heads Home 所佔的記憶體空間 : "Cabbage Heads Home" object deletes,3 left 範疇法則 (Scope rules) 控制物件的存在與否 兩個陣列元素是自動配置的變數, 所以離開定義它們的區段後, 它們就不存在了, 此例為 main() 而變數則在程式結束時清除 End of main() "Celery Stalks at Midnight" object deleted, 2 left "Lettuce Prey" object deleted, 1 left "Spinach Leaves Bowl for Dollars" object deleted, 0 left 再論 new 和 delete 上例中, 程式在兩個層次中用 new 和 delete 首先, 產生物件時, 建構函數中用 new 配置空間儲存每個物件的名稱字串, 相對應的在解構函數中利用 delete 釋放此記憶體空間 因為每個字串是一個字元陣列, 所以 delete 要加上 [ ] 當物件不存在時, 此記憶體空間即自動釋放 其次, 在 callme2() 函數中, 用 new 配置整個物件所需空間, 意即 str 指標和 len 成員的記憶體空間 產生物件, 呼叫建構函數, 此函數會配置空間儲存字串, 並且將此空間位址設給 str 當不需要此物件時, 程式利用 delete 11-9

C++ Primer Plus 5/e 中文精華版 將其刪除, 它僅釋放存 str 指標和 len 的空間, 而 str 所指的字串空間由解構函數負責清除 再一次強調解構函數所做的事 : 1. 若物件為自動配置的變數, 當程式結束定義此物件的區段, 就會呼叫其解構函數 所以程式結束 main() 時, 為 headlines[0] 和 headlines[1] 呼叫解構函數 ; 而結束 callme1() 時, 為 grub 呼叫解構函數 2. 若物件為靜態變數 ( 外部, 靜態或外部靜態 ), 當程式結束時, 即呼叫其解構函數, 此範例為 sports 物件 3. 若由 new 產生的物件, 則在 delete 該物件時, 才會呼叫其解構函數, 在 callme2() 中產生的物件, 在 main() 中才會刪除 此例中 new 和 delete 置於不同的函數有潛在的問題, 因為程式設計者須記得刪除這些物件 例如, 當程式作如下的修改 : String *ps; for ( int i = 0; i < 100; i++ ) ps = callme2(); cout << *ps << "\n"; delete ps; 這程式碼會產生 100 個不同的物件, 每次迴圈都將 ps 指向最新的物件, 且沒有記錄以前物件位址, 最後程式只刪除最近產生的物件, 其它 99 個物件佔住了記憶體, 而程式卻無法存取 適當地設計解構函數可在刪除物件時, 避免在物件內部中殘留記憶體, 但對呼叫 new 產生的物件, 仍須呼叫 delete 刪除物件 11-10

第 11 章 類別和動態記憶體配置 String 類別的問題 目前對 String 類別的定義並不完全, 它沒有實作很多有用的成員函數, 如多型 < == 和 > 運算子作字串比較 但此類別的問題更重要, 我們利用範例程式 11-4 為例, 說明有哪些問題存在 範例程式 11-4 problem1.cpp // problem1.cpp -- uses a function with a String argument // compile with strng1.cpp #include <iostream> using namespace std; #include "strng1.h" void showit(string s, int n); int main(void) String motto("home Sweet Home"); showit(motto, 3); system("pause"); return 0; void showit(string s, int n) // show String s n times for (int i = 0; i < n; i++) cout << s << "\n"; 範例中傳遞一個 String 物件和顯示次數至 showit() 函數, 其輸出結果如下 : 1: "Home Sweet Home" object created Home Sweet Home Home Sweet Home Home Sweet Home "Home Sweet Home" object deleted, 0 left "Home Sweet Home" object deleted, -1 left Null pointer assignment 11-11

C++ Primer Plus 5/e 中文精華版 從輸出結果可以得到一些奇怪的資訊 首先只產生一個物件, 但卻刪除了 2 個物件, 在記憶體中有 -1 個物件! 然後是一個指定空指標的訊息 ( 是否會顯示此訊息視編譯程式而定, 不管有無訊息, 都存在此基本的問題 有些編譯程式會當機 ) 下面是另一個問題的範例 : 範例程式 11-5 problem2.cpp // problem2.cpp -- initializes one string to another // compile with strng1.cpp #include <iostream> using namespace std; #include "strng1.h" int main(void) String motto("home Sweet Home"); String ditto(motto); // initialize ditto to motto cout << motto << "\n"; cout << ditto << "\n"; return 0; 程式中用 String 物件初始化另一個物件, 如下面輸出所示, 但仍出現前一個範例的問題 : 1: "Home Sweet Home" object created Home Sweet Home Home Sweet Home "Home Sweet Home" object deleted, 0 left "Home Sweet Home" object deleted, -1 left Null pointer assignment 11-12