標籤:c++
C++類模板的分離編譯
過去很多類模板都是整個類連同實現都放在一個標頭檔裡,像STL庫就是遵循這樣的策略來實作類別模板的。現在的標準正試圖矯正這種局面。
在實現中又許多函數模板。這意味著每個函數都必須包含模板聲明,並且在使用範圍操作符的時候,類的名稱必須通過模板變數來執行個體化。
比如一個operator=的代碼:
template <typename Object>const MemoryCell <Object> &MemoryCell<Object>::operator=(const MemoryCell<Object> & rhs){ if(this != &rhs) storeValue = rhs.storedValue; return *this;}
標頭檔內容
將聲明和實現都放在標頭檔中,對於類是行不通的,因為如果幾個不同的源檔案都有處理這個標頭檔的包含指令的話,就會出現重複定義函數的情況。但是,如果在標頭檔裡的知識模板而不是真實的類,就不會有問題。
函數對象
在一個函數的編寫中,需要像接受參數一樣接受比較函數,用該比較函數來決定兩個對象的大小。
使用這種方法的原因是,將比較的規則從對象中剝離出來,用一個比較函數來決定。
一個如傳遞參數一樣傳遞函數的巧妙辦法是:定義一個包含零個資料和一個成員函數的類,然後傳遞這個類的執行個體。從效果上看就是,通過將其放在對象中實現了函數的傳遞,該對象通常稱為函數對象。
舉例:
findMax函數獲得第二個形參,該形參為泛型型別。為使findMax模板可以無誤地擴充,泛型型別必須含有名為isLessThan的成員函數。該成員函數獲得第一個泛型型別的兩個形參,並返回一個bool值。
template <typename T, typename Comparator>const T & findMax(const vector<T> & arr, Comparator cmp){ int maxIndex = 0; for(int i=1;i<arr.size();i++) if(cmp.isLessThan(arr[maxIndex],arr[i])) maxIndex = i; return arr[maxIndex];}class CaseInsensitiveCompare{public: bool isLessThan(const string & lhs, const string & rhs) const { return stricmp(lhs.c_str(), rhs.c_str()) < 0; }};int main(){ vector<string> arr(3); arr[0] = "ZERbad"; arr[1] = "alligator"; arr[2] = "crocodile"; cout << findMax(arr, CaseInsensitiveCompare()) << endl; return 0;}
智能指標
包含指標的類需要特別注意複製控制,原因是複製指標時只複製指標的地址,而不會複製指標指向的對象。
當類中有指標成員時,一般有兩種方式來管理指標成員:一是採用值型的方式管理,每個類對象都保留一份指標指向的對象的拷貝;另一種更優雅的方式是使用智能指標,從而實現指標指向的對象的共用。
智能指標(smart pointer)的一種通用實現技術是使用引用計數(reference count)。智能指標類將一個計數器與類指向的對象相關聯,引用計數跟蹤該類有多少個對象的指標指向同一對象。
管理指標成員
設計具有指標成員的類時,類的設計者必須首先需要決定的是該指標應提供什麼行為。將一個指標複製到另一個指標時,兩個指標指向同一對象。當兩個指標指向同一對象時,可能使用任一指標改變基礎對象。類似地,很可能一個指標刪除了一個對象時,另一指標的使用者還認為基礎對象仍然存在。
大多數C++類採用以下三種方法之一管理指標成員:
- 指標成員採用常規指標型行為。這樣的類具有指標的所有缺陷但無需特殊的複製控制。
- 類可以實現所謂的“智能指標”行為。指標所指向的對象時共用的,但類能夠防止懸垂指標。
- 類採用值型行為。指標所指向的對象時唯一的,由每個類對象獨立管理。
懸垂指標
具有指標成員且使用預設合成複製建構函式的類,因為類的複製時,直接複製指標。使用者必須保證只要類對象存在,該指標指向的對象就存在。如果之前指向的對象不再存在了,此類指標稱為懸垂指標。結果未定義,往往導致程式錯誤,難以檢測。我們可以引入智能指標來防止懸垂指標的出現。
關於智能指標的具體內容請移步智能指標類和OpenCV的Ptr模板類
non-const成員函數調用const成員函數
在operator[]函數的編寫中,我們要做的其實是實現其機能一次,並使用它兩次。
你必須令其中一個調用另一個。這促使我們將常量性轉除。
當const operator[]完全做了non-const版本該做的一切時,如果將傳回值的const轉除是安全的,因為不論誰調用non-const operator[]都一定有個non-const對象,否則就不能調用non-const函數。所以令non-const operator調用其const函數式一個避免代碼重複的安全做法。
class A{public: const char& operator[](std::size_t position) const { ... ... return text[position]; } char& operator[](std::size_t position) { return const_cast<char &>( static_cast<const A&>(*this)[position] ); }private: std::string text;}
要讓non-const operator[]調用const operator[],但non-const operator[]內部如果只是單純調用operator[],會遞迴調用自己。所以我們必須明確指出調用的是const operator[]。這裡將*this從原始類型A&轉型為const A&。這樣為 *this 添加const。這裡我們使用static_cast進行安全轉型。
第二次則是從const operator[]的傳回值中移除const。這裡只能藉助const_cast完成。
如果在const函數內調用non-const函數,就會改變對象的邏輯狀態,所以const成員函數調用non-const成員函數是一種錯誤行為;而non-const成員函數本來就可以對其對象做任何動作,所以在其中調用一個const成員函數並不會帶來風險。
轉載請註明作者Jason Ding及其出處
Github部落客頁(http://jasonding1354.github.io/)
CSDN部落格(http://blog.csdn.net/jasonding1354)
簡書首頁(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)
百度搜尋jasonding1354進入我的部落客頁
【C++】C++問題——類模板分離編譯、函數對象、智能指標