2008/4/22 課後輔導 實作拷貝建構子與多載運算子
Post on 03-Jan-2016
53 Views
Preview:
DESCRIPTION
TRANSCRIPT
本週實習程式本週實習程式請實作一個類別 CppString ,用以儲存字串
並操作字串相關運算, private 成員資料中請以 char * 來實作此類別。
在類別中實作以下成員函數:◦建構子、解構子與拷貝建構子◦ 多載運算子「 operator== 」◦多載運算子「 operator=」◦ length() :回傳字串長度。◦ 多載串流擷取運算子「 >> 」◦多載串流插入運算子「<<」
練習一:建立類別練習一:建立類別請建立類別設計的架構
◦ 成員變數: 依題意,加入一個指向字元陣列的指標變數,另外,需要額外的變
數以記住陣列的大小◦ 成員函式
預設建構子 以輸入的字串作為初始值,欲設為空字串;例如:CppString(const char *s = “”);
解構子 測試函式 Output :用以列印成員變數,檢查資料是否正確
請撰寫簡單的 main 函式,編譯你的類別並測試物件的建立是否正確。
注意 char* 型態的字串處理需加入標頭檔<cstring>
Shallow CopyShallow Copy如果類別沒有給予拷貝建構子或多載賦值運算子( =
),其預設的物件拷貝是將其成員內容逐一的複製。◦ 範例:
class BigObj { char buf[100]; int i; };
void main(){ BigObj A, B; A = B;}
char buf[100] int i
物件 B
物件 A
Shallow CopyShallow Copy 如果當類別中有成員變數是指標變數的時候,考慮以下類別:
class BigObj2 { public: BigObj2(int s=100) { capacity = (s > 0)? s : 100; buf = new char[capacity]; } ~BigObj2() { delete [] buf; }private: int capacity; char *buf;};
Shallow CopyShallow Copy在預設的情況下,兩個指標變數的拷貝僅僅拷貝了記
憶體位址,非針對實體的資料進行拷貝。
30000
char *bufint capacity
物件 B
38000
物件 A
Shallow Copy
30000
38000
30000
Shallow CopyShallow Copy 的問題的問題考慮以下程式碼將發生的情形:
◦ func() 接受一個物件做為參數,再將此物件回傳。
◦ main 函式宣告兩個物件 A 與 B ,並將 A 傳入func 後,以 B 物件來接受其回傳值。
BigObj2 func(BigObj2 X){ return X;}
void main(){ BigObj2 A, B; B = func(A);}
?
A X B
main() main()func()
暫存的物件 T
Copy Copy Copy
operator=
Shallow Copy 的問題
30000
char *bufint capacity
物件 T
38000
物件 B
30000
38000
30000
Dangling Pointer
Memory Leak
自訂拷貝建構子與多載賦值運算子自訂拷貝建構子與多載賦值運算子使用時機
◦當成員變數含有指標變數的時候,最好提供自訂以下函式: 預設建構子
不管什麼時機,都盡量提供一個預設建構子 自訂的拷貝建構子 多載賦值運算子( operator= )
自訂拷貝建構子自訂拷貝建構子呼叫時機:
◦ 從一個既存物件 A 中建立一個新的物件,且此物件為 A 的複本。
函式原型:◦ 沒有回傳值◦ 一定只有一個參數,例:
const BigObj2 & 一定是一個 const 的參考指向傳入的物件;代表該傳入的物
件不可被修改。
自訂拷貝建構子自訂拷貝建構子class BigObj2 // 因篇幅有限故直接寫入類別中,實作時請將函式原型{ // 與實作分開public: … BigObj2(const BigObj2 &source) { capacity = source.capacity; buf = new char[capacity]; for (int i=0; i<capacity; i++) buf[i] = source.buf[i]; }private: …};
直接複製 capacity直接複製 capacity
根據 capacity ,再次new 一個新的陣列
根據 capacity ,再次new 一個新的陣列
逐一複製陣列中的元素逐一複製陣列中的元素
練習二:加入拷貝建構子練習二:加入拷貝建構子請於類別中加入拷貝建構子請撰寫簡單的 main 函式,編譯你的類別並
測試物件的建立是否正確。 例:
CppString A(“hello”);CppString B = A;B.Output();
多載賦值運算子多載賦值運算子呼叫時機:
◦ 將一個既存物件 B 指定給另一個既存的物件 A 的時候 呼叫賦值運算子( operator= )
BigObj A, B;A = B; //等同於呼叫 A.operator=(B);
與實作拷貝建構子的差別◦ 因為賦值運算子是拷貝到一個「已經存在」的物件
,表示原物件中已經有一些資料存在,故需增加一個程式碼來對舊有資料作清除,且需要判斷指定的物件是否就是自己本身。
多載賦值運算子多載賦值運算子class BigObj2 { public: … BigObj2 &operator=(const BigObj2 &source) { if (this == &source) // 判斷指定的物件是否就是自己本身 return *this; // 例如: A = A; if (buf != NULL) // 將自己所指向的記憶體釋回 delete [] buf; capacity = source.capacity; // 以下同拷貝建構子的內容 buf = new char[capacity]; for (int i=0; i<capacity; i++) buf[i] = source.buf[i]; return *this; // 將自己本身回傳 }private: …};
多載賦值運算子多載賦值運算子介面說明;以類別 BigObj 為例:
◦參數: const BigObj &source 接受一個和自己同型態的物件參考,且指定
const ,代表傳入的物件不可修改。 注意傳入參考使得副函式具有更改到原物件的能力,加上 const 可保證傳入的物件不會被副函式任意更動。
通常賦值運算子會把自己( *this )回傳,回傳的型態是一個指向該物件的參考。這樣做的目的是為了實現「連鎖」的指定,例如:A = B = C; 此行敘述等同於
A.operator=(B.operator=(C));
練習三:加入練習三:加入 operator=()operator=()請於類別中多載賦值運算子請撰寫簡單的 main 函式,編譯你的類別並
測試物件的建立是否正確。 例:
CppString A(“hello”);CppString B(“world”);B.Output();B = A;B.Output();
多載串流插入運算子多載串流插入運算子
◦例如:cout << classObject
◦因為串流插入運算子的左邊是串流輸出物件,型態為 ostream & ,故不能把他以類別的成員函式的方法多載 因此必須以副函式的方式來多載。 同樣,在多載串流擷取運算子的時候也是一樣要用
副函式的方式多載。
範例範例
class Complex { friend ostream &operator<< (ostream &out, Complex &P); public: … private: int real, imaginary; };
ostream &operator<<(ostream &out, Complex &P){ out << P.real << “+” << P.imaginary << “i”; return out;}
範例範例In main():
#include <iostream>
using namespace std;
int main() { Complex P, Q; … cout << P; cout << P << Q; return 0; }
top related