C ++ 编程思想(卷二) 笔记
生活随笔
收集整理的這篇文章主要介紹了
C ++ 编程思想(卷二) 笔记
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2013年8月16日夜02:53
第一章
1、異常處理是C++的主要特征之一
2、assert():用于開發階段調試,#define NDEBUG 使得assert()失效。
3、C語言中錯誤處理信息:1-在函數中返回錯誤信息。2-使用C庫的信號處理系統signal(),raise()。3-使用C庫的setjmp()和longjmp().信號處理方法和setjmp、longjmp函數不調用析構函數,對象不能被正確清理。
4、throw 創建程序所拋出對象的一個拷貝。throw表達式返回這個對象。try/catch異常處理器可以處理某種異常類型和這種異常類型的派生類。并且為避免再次拷貝異常對象,最好通過引用而不是通過值來捕獲異常。通過引用來捕獲異常,基類異常處理器能夠捕獲派生類異常。
5、catch(...){}可以捕獲任何類型的異常。在異常處理器內部catch(){throw;}使用不帶參數的throw可以重新拋出異常。
6、如果沒有一個異常處理器可以捕獲異常,terminate()會自動被調用。<exception>中定義.terminate調用abort(),導致程序不會正常終止函數,全局對象額靜態對象的析構函數不會執行。terminate也會在下面兩個條件下執行:局部對象的析構函數拋出異常時,棧正在進行清理工作;或者是全局對象或靜態對象的構造函數或析構函數拋出異常。set_terminate函數可以設置自己的terminate函數,自定義的terminator不能有參數,且返回值為void類型。第一次調用返回默認的terminate函數的指針,這樣可以在需要的時候恢復原來的terminate。
7、防止資源泄漏,采用方式:1-在構造函數中捕獲異常,用于釋放資源。2-在對象的構造函數中分配資源,并在對象的析構函數中釋放資源。
8、若返回類型為引用,則表示不能返回0.因為不能有空引用。
9、RAII,資源獲得式初始化,是一個封裝類,用于封裝指向分配的對內存的指針,使得程序能夠自動釋放這些內存。auto_ptr類模板在頭文件<memeroy>中定義。auto_ptr<類>
10、從exception類(定義在頭文件<exception>中)派生自己定義的標準異常類。兩個派生類:logic_error和runtime_error。這兩個類在頭文件<stdexcept>文件中,派生類接受參數std::string的構造函數。通過exception::what()函數,可以得到保存的消息。因為exception沒有參數為string的構造函數,所以用戶最好從兩個派生類派生自己定義的類。
11、異常規格說明:int fun () throw(big,small);函數可能拋出的異常寫在throw之后的括號中。int fun()throw()表示函數不會拋出任何異常。int fun()表示函數有可能拋出任何類型的異常。如果異常不在異常規格說明的異常集中,那么unexcepted會被調用。默認的unexpected調用terminate函數。可以使用set_unexpected函數(頭文件<exception>)使用一個函數指針作為參數,這個指針指向的函數沒有參數,而且其返回類型為void。set_unexpected函數返回unexpected先前的值,所以可以保存這個值,以后使用它。
12、異常規格說明中包含std::bad_exception 在<exception>中,則unexpected異常會被換成std::bad_exception對象,程序恢復到函數被調用的位置重新開始異常匹配。如果異常規格說明中不包含std::bad_exception,程序會調用terminate函數。VC++中不會調用set_unexpected()函數。
13、異常規格說明與繼承。派生類的異常說明應該在結構層次上低于基類的異常說明。即基類的異常說明--》派生出派生類的異常說明。異常規格說明是為非模板準備的。
14、operator=應遵守6個模式:1-確保程序不給自己賦值,如果是的話跳6。2-給指針數據成員分配新內存。3-從原有的內存區向新分配的內存區拷貝數據。4-釋放原有的內存。5-更新對象狀態,也就是把指向分配新堆內存地址的指針賦值給指針數據成員。6-返回*this。
15、當delete對象的時候,對象的析構函數會被調用。不要在析構函數中拋出異常 。
第二章
1、測試套件框架TestSuit,包含兩個主要的類:Test和Suit。Test類保存測試成功和失敗的次數??梢詮腡est類派生自己的測試對象。只需要重寫成員函數run()即可,并用test_()來測試。使用report函數顯示前面的輸出。
2、Test類有3個虛函數:虛析構函數、reset函數,純虛函數run;基類設置虛析構函數,表示可以通過基類指針釋放在堆上分配的派生類對象。
3、利用宏處理代碼跟蹤:這兩個宏可以完成調試器的基本功能,跟蹤代碼執行,并顯示表達式的值。
#define TRACE(ARG) cout<<#ARG<<endl; ARG ? 變種:#define TRACE(ARG) cout<<#ARG"=["<<ARG<<"]"<<endl;
第三章?
1、C++字符串string極大減少了C語言編程中3中最常見的錯誤:1-數組越界。2-通過未被初始化或被賦以錯誤值的指針來訪問數組元素。3-在釋放了某一數組原先所分配的存儲單元后仍保留了懸掛指針。
2、創建string對象:1-創建空string對象。2-將字符串傳給構造函數。3-用string=字符串方式初始化。4-用一個string初始化另一個string。
3、string s2(s1,0,8)從s1[0]開始的8個字符賦值給s2.
4、string s; s.substr(20,10)提取從s[20]開始的10個字符。
5、使用迭代器。string source; string s(source.begin(),source.end());
6、string s(5,'a'),用5個字符'a'初始化s.
7、對字符串操作:string s, 大小:s.size(),s.length(). 當前分配的存儲空間的規模:s.capacity()。從s[1]處插入:s.insert(1,"hua");s.insert(8,tag+' ')從末尾追加:s.append("dfd");
替換:s.replace(4,5,"dfdf");從s[4]開始替換5個字符。查找:i=s.find(tag,0)從s[0]開始查找,默認find(tag)從0開始查找。返回size_t,判斷查找到末尾:if(i==string::npos)
替換:replaceAll(s,"hua","XXX")將S的所有hua字符串替換為XXX
8、string::replace()一次只替換一次。算法replace(s.begin(),s.end(),'X','Y')將某個字符全部用另一個字符換掉。s=s1+s2.使用operator + 和operator +=
9、字符串查找:find,rfind,find_first_of找第一個與指定數值中任一個字符匹配,find_last_of,find_first_not_of,find_last_not_of
10、toupper,tolower(char),<cctype>
11、反向查找s.rfind.從后向前查找,不成功返回string::npos
12、從字符串中刪除字符:s.erase(0,string::npos),兩個默認值,起始位置,刪除的個數。s.erase()刪除所有。
13、<string>全局函數getline(in,s).使用s.c_str()可以將string類型轉換成char * 類型,
14、字符串比較string first,string second. first.compare(second)==0,>0.<0, first.compare(1,7,second,1,7),比較first從first[1]開始的7個字符和second[1]開始的7個字符。s[n]和s.at(n)一樣,但是s.at(n)可以拋出異常out_of_range()。
15.字符串交換:first.swap(second)
16、文件操作:string fname,刪除文件:remove(fname.c_str()),目錄存在:if(!exist(fname)).ifstream in(fname.c_str()),if(!in){exit(EXIT_FAILURE);}
第四章
1、#ifndef #define #endif 最主要的目的是防止頭文件被重復包含與編譯。
2、1-while(fgets(buf,size,fp));2-fputs(buf,stdout);3-while((c=fgetc(fp))!=EOF);4-fputc(c,fp);5-fread(buf,size,count,fp),每個count有size個字符,6-fwrite(buf,size,count,fp);7-while(!feof(fp));8-fflush(fp);9-fseek(fp,offset,fromwhere);formwhere:SEEK_SET:0,SEEK_END:2,SEEK_CUR:1;10-ftell(fp);文件指針相對文件首偏移字節數。
11-rewind(fp);將指針移至文件開頭。12-memcpy(newdata,data,sizeof()*count);
3、ostream &os;char fillc=os.fill('0');填充'0',返回先前的填充。os<<setfill(fillc)<<endl;恢復先前的填充。os<<setw(4)<iomanip>
4、按行輸入:成員函數get(),成員函數getline(),全局函數getline ()。1-2-有三個參數:指向緩沖區的指針、緩沖區大小、結束字符:默認'\n',while(in.get(buf,SZ) in.get(); 第一個get讀SZ-1個字符,遇到結束符。用get()跳過輸入流中的結束符。或者使用ignore(int N=1,char c=EOF),跳過的字符的數目,默認為1,遇到C字符時,函數退出,默認為EOF。 ? while(in.getline(buf,SZ)在遇到結束字符的時候,兩個都會在結果緩沖區末尾存儲一個0,區別:當遇到結束符,get停止執行,不從輸入流中提取結束符。再調用get,會遇到同一個結束符,函數將立即返回而不會提取輸入。而getline相反,將從輸入流中提取結束符,但仍然不會把它存儲到結果緩沖區中。get的重載版本:1-int get(),沒有參數,返回int類型的下一個字符.2-get(char &),從流中讀取一個字符放到這個參數中。3-把流對象存儲到基礎的緩沖區中。
5、處理流錯誤:ios::badbit,發生致命錯誤,流不能繼續使用。ios::eofbit,輸入結束,ctrl+D. ios::failbit,輸入非法數據,流可以繼續使用。ios::goodbit:正常。清空標志位:myStream.clear();設置某個標志位,重置另一個標志位:myStream.clear(ios::failbit|ios::eofbit);
6、打開剛創建的文件:ofstream out("file.out");ifstream in("file.out");對同一個文件打開讀寫要確定關閉文件。方法:一、使用大括號,是對象離開作用域調用析構函數,{out}in.二、使用out.close().
7、文件打開模式:ios::in打開輸入文件ios::out打開輸出文件ios::app打開一個僅用于追加的輸出文件ios::ate打開一個已存在的文件,并將文件指針指向文件末尾ios::trunc如果文件存在,截斷舊文件。ios::binary以二進制方式打開文件。回車/換行:0x0D/0x0A
8、流緩沖:streambuf對象將<<右側對象中的字符輸出到左側的對象中, ?ifstream in("i.cpp");cout<<in.rdbuf();輸出文件內容到標準輸出。rdbuf返回一個指針。
9、ios::beg流的開始位置,ios::cur流的當前位置,ios::end流的末端位置,p表示寫指針,g表示讀指針。ostream 調用tellp,seekp. ?istream 調用tellg,seekg. ? ofstream out("",ios::out|ios::binary);out.write(origData[i],STR_LEN). ifstream in("",ios::in|ios::binary);in.read(readData[i],TR_LEN);in.seekg(-STR_LEN,ios::end);//Seek -STR_KEN byte from the end of file
10、char readData[STR_NUM][STR_LEN]={{0}};
11、字符串輸入輸出流iostringstream直接對內存而不是對文件和標準輸出設備進行操作。它使用和cin、cout、相同的讀取和格式化函數來操縱內存中的數據。寫字符到字符串流,使用ostringstream,從這個流讀取字符,可以使用istringstream。頭文件<sstream>.epsilon()返回在<limits>中定義的常量,表示雙精度數字的機器誤差。 istringstream s("47 1.414 this is a test");int i;double f;s>>i>>f; ?ostringstream將字符串插入動態緩沖區,調用str()將內存中字符串轉換string。 cin >> ws; // Throw away white space
? string stuff;
? getline(cin, stuff); // Get rest of the line
? cout<<stuff;
? ostringstream os;
? os << "integer = " << i << endl;
? os << "float = " << f << endl;
? os << "string = " << stuff << endl;
? string result = os.str();
? cout << result << endl;
重載版本的os.str("huaguanglu");cout<<os.str()<<endl;
12、格式化標志:setf()打開標志,unsetf()關閉標志。標志:ios::skipws 跳過空格 ios::showbase打印整型時指出數字的基數(dec/oct/hex)。ios::showpoint顯示浮點值的小數點。ios::uppercase顯示十六進制時,使用大寫。ios::showpos:顯示正數前面的'+'。ios::unitbuf:單元緩沖區。立即刷新。cout.setf(ios::showpos)顯示正號cout.unsetf(ios::showpos)關閉顯示。
13、格式化域:ios::basefield (ios::dec使整型數值的基數為10,ios::hex使整型數值的基數為16,ios::oct使整型數值的基數為8).ios::floatfield(ios::scientific以科學計數的形式顯示浮點數.ios::fixed以固定格式顯示浮點數。)ios::adjustfield(ios::left使數值左對齊,填充字符右邊空位。ios::right,ios::internal填充字符放在正負號和數值之間正負號左對齊,數值右對齊),cout.setf(ios::fixed,ios:floatfield);
14、寬度、填充、精度:int ios::width()返回當前寬度,默認為0,in ios::width(int n)設定寬度,返回先前。,int ios::fill()返回當前填充字符,默認為空格,int ios::fill(int n)設定填充字符,返回先前,int ios::precision()默認精度小數點后面六位,int ios::precision(int n)設定精度,返回先前。寬度不會截斷字符。寬度只規定最小長度。
cout.setf(ios::boolalpha)返回true、false,代替0,1
15、操縱算子:刷新流:cout<<endl;cout<<flush;在頭文件<iostream>中包含操縱算子:cin>>ws吃掉空格,cout<<hex<<"0x"<<i<<endl;
endl,flush,hex,oct,dec,
showbase,noshowbase,showpos,noshowpos,showpoint,noshowpoint,
uppercase,nouppercase,skipws,noskipws,
left,right,internal,
scientific,fixed,
setiosflags()相當于ios::setf(),resetiosflags,相當于ios::unsetf(),
setbase(base n),n取值8,10,16,輸出的基數。setfill(char n)相當于ios::fill(n),
setprecision(int n),setw(int n),?
舉例:
istringstream is("one 2.34 five");
? string temp;
? is >> setw(3) >> temp;temp="on".is >> setw(2) >> temp;temp="e".
16、const ulong ULMAX=numeric_limits<ulong>::max();<limits>
17、time_t timer;srand(time(&timer));
第五章
1、模板參數:無類型模板參數template<class T,size_t N> class Stack{
T data[N];
size_t count;
public:
void push(const T& t);
};
實例化模板時,為參數N提供一個編譯時常數Stack<int ,100> myStack;
2、默認模板參數:template<class T, size_t N=100> class Stack{};實例化:Stack<int> stack;
template<class T, class Allocator=allocator<T> > class vector;vector有兩個參數,一個參數表示它持有的包含對象的類型。另一個參數表示vector所使用的分配器。
函數模板不可以使用默認的模板參數。卻能使用模板參數作為普通函數的默認參數。
template<class T> T sum(T* b, T* e, T init=T()){}main{int a[]={1,2,3};cout<<sum(a,a+sizeof a/ sizeof *a)<<endl;}參數默認是T(),int()執行零初始化。int a=int();
3、模板類型的模板參數:
template<class T> class Array{
public:
void push_back(const T& t){}
T* begin(){return data;}
T* end(){return data+count;}
};
template<clss T, template<class> class Seq> class Container{
Seq<T> seq;
public:
void append(const T& t){seq.push_back(t);}
T* begin(){return seq.begin();}
T* end(){rturn seq.end();}
}
main()
{
Container<int, Array> container;
}
此時的默認參數需要重新聲明:
template<class T, size_t T=10>
template<class T,template<class,size_t = 10> class Seq>
typename Seq<T>::iterator begin(){return seq.begin();}
typename T::id i;
typename作用:
1-typename不是創建一個新類型,而是聲明id是一個嵌套類型,然后創建這個類型的對象 i。
class Y{
class id {};
};
創建一個新類型:typedef typename Seq<It>::iterator It;
2-typename代替class
4、<typeinfo> cout<<typeid<T*this>.name(),返回type_info對象。成員模板函數不能為virtual虛類型。
5、min<>(1,4),表示強制使用模板。<cctype>toupper/tolower有一個參數,<iostream>toupper/tolower有兩個參數。避免沖突:
1-可以在不包含<iostream>中使用,2-可以使用封裝的函數模板:template<class charT> charT strTolower(charT c){return tolower(c);}3-可以使用類型轉換:
transform(s.begin(),s.end(),s.begin(),static_cast<int (*)(int)>tolower);
6、模板特化:template<>告訴編譯器接下來是模板特化。template<> const char* const&min<const char*>(const char* const &a,const char* const &b){retrn (strcmp(a,b)<0)?a:b;}
7、模板不是代碼,是產生代碼的指令。防止模板代碼膨脹:一個模板化的Stack容器,用int,int *,char*特化,會生成3個版本的Stack代碼,可以用void* 進行完全特化,然后從void*實現中派生出所有的其他的指針類型。
8、名稱查找問題:編譯器第一階段對模板解析,第二階段模板實例化。限定名:MyClss::f()/x.f()/p->f() ?關聯參數查找。若名稱是關聯的,則它的查找在實例化進行,非限定的關聯名稱除外,它是一個普通的名稱查找,它的查找在定義時進行,比較早。
9、在類中聲明一個友元函數,就允許一個類的非成員函數訪問這個類的非公有成員。友元函數模板必須提前聲明。
template<class T> class Friendly;
template<class T> void f(const Friendly<T>&);
template<class T> class Friendly{
public:
friend void f<>(const Friendly<T>&);//f<>說明f是一個模板
friend void f(const Friendly<T>&);//f說明f是一個普通函數
};
10、模板元編程:編譯時編程,在程序開始運行前就已經完成了計算。
template<int n> struct Factorial {
? enum { val = Factorial<n-1>::val * n };
};
template<> struct Factorial<0> {
? enum { val = 1 };
};
int main() {
? cout << Factorial<12>::val << endl; // 479001600
} ///:~
11、模板定義的源代碼與它的聲明相分離,使用export關鍵字。
export
template<int n> struct Factorial {
? enum { val = Factorial<n-1>::val * n };
};
第六章
<algorithm>
1、copy: int a[SZ];int b[SZ];copy(a,a+SZ,b); ? ? equal: equal(a,a+SZ,b);相等返回true.
2、vector<int> v1(a,a+SZ),v2(SZ),v3;//構造函數初始化,v1用a初始化,v2用SZ作分配空間大小。v3沒有確定大小。equal/copy(v1.begin(),v1.end(),v2.begin()),
copy(v1.begin(),v1.end(),back_inserter(v3)), back_inserter在頭文件<iterator>中定義。 ?equal(v1.begin(),v1.end(),v3.begin())
3、取<=15的數:bool gt15(int x){return 15<x;} ?remove_copy_if(a,a+SZ,b,gt15);返回忽略gt15為真的值(b.end())。bool contains_e(const string&s){return s.find('e') != string::npos;} ?replace_copy_if(a,a+SZ,b,contains_e,string("kiss")); ?返回b.end() ? ? ? ? replace_if(a,a+SZ,contains_e,string("kiss"))
4、流迭代器:<iterator> ostream_iterator<int>(cout,'\n').?
?remove_copy_if(a,a+SZ,ostream_iterator<int>(cout,'\n'),gt15);?
ofstream out();remove_copy_if(a,a+SZ,ostream_iterator<int>(out,'\n'),gt15);?
ifstream in(); remove_copy_if(istream_iterator<int>(in),istream_iterator<int>(),ostream_iterator<int>(cout,'\n'),gt15); ?
istream_iterator<int>()默認構造函數表示文件的結束
5、size_t n=count_if(a,a+SZ,gt15);產生>15的整數元素的個數。int *p=find(a,a+SZ,20)搜索一個序列,直到遇到等于第三個參數的元素。返回第一次找到的指針位置。
6、函數對象:它實現起來像函數同時保存狀態,使用時卻不用考慮函數參數的個數。這種抽象叫函數對象。
<functional> 提供兩種類型:unary_function和binary_function.
?remove_copy_if(a,a+SZ,b,bind2nd(greater<int>,15))
int a[]={12,20,12};count_if(a,a+SZ,not1(bind1st(equal_to<int>(),20)))
產生標準函數對象的模板表達式:類型:unary_function和binary_function.
plus<int>() /minus/multiplies/divides/modulus/negate/equal_to/not_equal_to/greater/less/greater_equal/less_equal/logical_and/logical_or/logical_not
tansform(v.begin(),v.end(),v.begin(),bind2nd(multiplies<int>(),10))用10乘以v中每個元素
7、sort(a,a+SZ),
8、函數指針適配器ptr_fun()把一個指向函數的指針轉化成一個函數對象。
int d[] = { 123, 94, 10, 314, 315 };
const int DSZ = sizeof d / sizeof *d;
bool isEven(int x) { return x % 2 == 0; }
int main() {
? vector<bool> vb;
? transform(d, d + DSZ, back_inserter(vb),
? ? not1(ptr_fun(isEven)));
? copy(vb.begin(), vb.end(),
? ? ostream_iterator<bool>(cout, " "));
? cout << endl;
? // Output: 1 0 0 0 1
} ///:~
9、for_each(v.begin(),v.end(),mem_fun(&shape::draw))算法將序列中每一個元素一次傳遞給第三個參數指示的函數對象。mem_fun()使用指向成員的一個指針來作為它的參數。mem_fun_ref()為一個對象直接調用成員函數
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
#include "../purge.h"
using namespace std;
class Shape {
public:
? virtual void draw() = 0;
? virtual ~Shape() {}
};
class Circle : public Shape {
public:
? virtual void draw() { cout << "Circle::Draw()" << endl; }
? ~Circle() { cout << "Circle::~Circle()" << endl; }
};
class Square : public Shape {
public:
? virtual void draw() { cout << "Square::Draw()" << endl; }
? ~Square() { cout << "Square::~Square()" << endl; }
};
int main() {
? vector<Shape*> vs;
? vs.push_back(new Circle);
? vs.push_back(new Square);
? for_each(vs.begin(), vs.end(), mem_fun(&Shape::draw));
? purge(vs);
} ///:~
class Angle {
? int degrees;
public:
? Angle(int deg) : degrees(deg) {}
? int mul(int times) { return degrees *= times; }
};
transform()要求它調用的函數返回一個值。不能使用transform()調用返回值為void的成員函數,也不能調用只有一個參數的for_each函數
int main() {
? vector<Angle> va;
? for(int i = 0; i < 50; i += 10)
? ? va.push_back(Angle(i));
? int x[] = { 1, 2, 3, 4, 5 };
? transform(va.begin(), va.end(), x,
? ? ostream_iterator<int>(cout, " "),
? ? mem_fun_ref(&Angle::mul));
? cout << endl;
? // Output: 0 20 60 120 200
} ///:~
vector<string>v;
vector<string>::ietrator it;
it=find_if(v.begin(),v.end(),mem_fun_ref(&string::empty));
10、string-->char *-->atof
transform(v.begin(),v.end(),back_inserter(b),mem_fun_ref(&string::c_str));
transform(b.begin(),b.end(),back_inserter(c),std::atof);
11、填充和生成: vector<string> v1(5); ?vector<string> v2;
fill(v1.begin(),v1.end(),"ddd"); ?fill_n(back_inserter(v2),7,"bye"); ? ? ? ?fill_n (first,n,value)對由first開始的n個元素賦值value。
generate(v1.begin(),v1.end,gen()); 對區間每個元素調用gen(). ? generate_n(back_inserter(v2),7,gen())對gen()調用7次,將返回值賦給由first開始的7個元素。
12、計數:count(v.begin(),v.end(), value); ?count_if(v.begin(),v.end(),pred)//pred返回true的個數
13、操作序列:
copy(v.begin(),v.end(),dest.begin())
copy_backward(v.begin(),v.end(),dest.begin())//以相反的順序復制元素
reverse(v.begin(),v.end())//倒置原序列范圍的元素
reverse_copy(v.begin(),v.end(),dest.begin())
swap_ranges(v.begin().v.end(),v2.begin())//交換相等大小兩個范圍的內容
rotate(first,middle,last)//[first,middle)內容移至末尾,[middle,last)元素移至開始
rotate_copy(first,middle,last,dest)
next_permutation(first,last)//排列成后繼的排列
next_permutation(first,last,pred)//重載
pre_permutation(first,last)//排列成前驅的排列
pre_permutation(first,last,pred)//重載
random_shuffle(first,last)//隨機重排范圍內的元素
random_shuffle(first,last,rand)//重載,使用用戶提供的隨機函數重排
partition(first,last,pred)//劃分
stable_partition(first,last,pred)
14、查找和替換:
find(first,last,value)
find_if(first,last,pred)//pred==true 返回
adjacent_find(first,last)//兩個鄰近相等
adjacent_find(first,last,pred)//查找相鄰兩個符合pred的元素
find_first_of(first1,last1,first2,last2)
find_first_of(first1,last1,first2,last2,pred)
search(first1,last1,first2,last2)//第二序列是否出現在第一序列范圍之內,順序完全一致,返回首先出現的位置
search(first1,last1,first2,last2,pred)//比較的每對元素是否使pred為true.
find_end(first1,last1,first2,last2)//第二序列是否是第一序列的子集,返回最后出現的位置
find_end(first1,last1,first2,last2,pred)//比較的每對元素是否使pred為true.返回最后出現的位置
search_n(first,last,value)//找count個連續的值,與value相等。
search_n(first,last,value,pred)//與value相等的值傳遞給pred,返回true,否則返回last.
min_element(first,last)//最小值首次出現的位置
min_element(first,last,pred)//返回r,對(first,r)范圍中的元素使pred(*e,*r)都為假。
replace(first,last,old_value,new_value);
replace_if(first,last,pred,new_value);
replace_copy(first,last,result,old_value,new_value);
replace_copy_if(first,last,result,pred,new_value);
15、比較范圍:
equal(first1,last1,first2)
equal(first1,last1,first2,pred)
lexicographical_compare(first1,last1,first2,last2)//范圍1序列元素小于范圍2序列元素
lexicographical_compare(first1,last1,first2,last2,pred)
pair<InputIterator1,InputIterator2> ? pair在頭文件<utility>中定義
mismatch(first1,last1,first2)//比較兩個序列從哪兒開始不同,將兩個位置裝入pair返回,用first,second訪問元素。
mismatch(first1,last1,first2,pred)//比較兩個序列從哪兒開始不同,將兩個位置裝入pair返回,用first,second訪問元素。
16、刪除元素:
remove(first,last,value)//返回new_last
remove_if(first,last,pred)//返回new_last
remove_copy(first,last,result,value)//返回new_last
remove_if(first,last,result,pred)//返回new_last
真正刪除:c.erase(remove(c.begin(),c.end(),value),c.end);
unique(first,last)//刪除相鄰的相等值的副本,使用unique前,需要先sort().
unique(first,last,pred)
unique_copy(first,last,result)
unique_copy(first,last,result,pred)
17、排序和運算
排序:
sort(first,last)//升序
sort(first,last,pred)
stablesort(first,last)//升序,保持相等元素的原始順序
stablesort(first,last,pred)
partial_sort(first,middle,last)//對一定數量元素排序,放入[first,middle)中,范圍[middle,last)元素不保證有序
partial_sort(first,middle,last,pred)
partial_sort_copy(first1,last1,first2,last2)
partial_sort_copy(first1,last1,first2,last2,pred)
nth_element(first,nth,last)//也是對一部分處理,[first,nth)元素滿足 operator <,而[nth,last)都不滿足。
nth_element(first,nth,last,pred)
運算:
在已排序的序列中查找
binary_search(first,last,value)
binary_search(first,last,value,pred)
lower_bound(first,last,value)//第一次出現的位置,若沒有,返回應該出現的位置
lower_bound(first,last,value,pred)
upper_bound(first,last,value)//超過value最后出現的位一個置,若沒有,返回應該出現的位置
upper_bound(first,last,value,pred)
pair<ForwardIterator1,ForwardIterator2> ? pair在頭文件<utility>中定義
equal_range(first,last,value)//結合了lower_bound和upper_bound,返回一個指出value在已排序的范圍[first,last)中首次出現和超越最后出現的pair,如果沒有找到,這兩個迭代器都指 //出value在該序列中應該出現的位置。
equal_range(first,last,value,pred)
合并已排序的序列
merge(first1,last1,first2,last2,result)
merge(first1,last1,first2,last2,result,pred)
inplace_merge(first,middle,last)//[first,middle)有序,[middle,last)有序,兩個合起來
inplace_merge(first,middle,last,pred)
在已排序的序列上進行集合運算
includes(first1,last1,first2,last2)//序列2是1的子集,返回true
includes(first1,last1,first2,last2,pred)//序列2是1的子集,返回true
set_union(first1,last1,first2,last2,result)//求并集,返回result.end().
set_union(first1,last1,first2,last2,result,pred)
set_intersection(first1,last1,first2,last2,result)//求交集,返回result.end().
set_intersection(first1,last1,first2,last2,result,pred)
set_difference(first1,last1,first2,last2,result)//求集合的差,返回result.end().
set_difference(first1,last1,first2,last2,result,pred)
set_symmetric_difference(first1,last1,first2,last2,result)//求在集合1不在集合2,在集合2不在集合1,返回result.end().
set_symmetric_difference(first1,last1,first2,last2,result,pred)
18、堆運算
make_heap(first,last)//建堆
make_heap(first,last,pred)
push_heap(first,last)//向范圍[first,last-1)堆中增加元素*(last-1)
push_heap(first,last,pred)
pop_heap(first,last)//將最大的元素 *first 放入位置*(last-1)并且重新組織剩余的范圍。
pop_heap(first,last,pred)
sort_heap(first,last)//轉化成普通的排列順序,是不穩定的排序
sort_heap(first,last,pred)
19、對某一范圍內的所有元素進行運算
UnaryFuntion for_each(first,last,UnaryFuntion f);//對范圍中每個元素應用f,最終返回值是f
OutIterator transform(first,last,OutIterator result,UnaryFunction f)//f(*first),返回result.end()
OutIterator transform(first,last,first2,OutIterator result,BinaryFunction f)//f(*first,*first2),返回result.end()
20、數值算法
<numeric>
T accumulate(first,last,T result)//i指向[first,last)中的每一個元素,result = result + *i
T accumulate(first,last,T result,BinaryFunction f)//i指向[first,last)中的每一個元素,對每個*i應用f(result,*i)
T inner_product(first1,last1,first2,T init)//init是內積的初始值,可能是0或其他值。廣義內積。{1,2,3} {2,3,4}=1*2+2*3+3*4=20
T inner_product(first1,last1,first2,T init,BinaryFunction op1,BinaryFunction op2)//init是內積的初始值。op1 代替加法,op2代替乘法{1,2,3} {2,3,4}=1 op2 2 op1 2op2..
partial_sum(first,last,result)//廣義部分和。{1,2,3} 即:{1,1+2,1+2+3}={1,3,6}
partial_sum(first,last,result,BinaryFunction op)//op代替 +?
adjacent_difference(first,last,result)//廣義部分差。{1,2,3} 即:{1,2-1,3-2}={1,1,1}
adjacent_difference(first,last,result,BinaryFunction op)//op代替 -?
21、通用
swap(a,b)
iter_swap(it1,it2)
min(a,b)
max(a,b)
第七章
1、vector 高效訪問。list 高效插入。
set<int> intset; int i=10;
intset.insert(i);//自動排序,并且元素唯一。
2、容器分類:
序列容器: vector、list、deque(雙端隊列) 都有push_back(),list和deque還有一個push_front(),vector和deque可以使用operator[],但是list不支持
容器適配器:queue、stack、priority_queue
關聯式容器:set、map、multiset、multimap
3、vector<string>::iterator it; reverse_iterator it=v.rbegin();rend();
4、back_inserter(v);front_inserter(v);it++,it++,inserter(v,it)
istream_iterator<char>和ostream_iterator<char>會吃掉空白字符。
使用:istreambuf_iterator<char>和ostreambuf_iterator<char>
5、序列容器:vector、list、deque
typedef Container ?C ; C s;
s.empty()//判空
s.size()//尺寸
s.max_size()//最大可能尺寸
s.front()//起始元素
s.back()//終止元素
C s(10,1)//10個1
int a[SZ];
c.assign(a,a+SZ);
c.assign(10,2)//10個2
c.push_back(47);
c.pop_back()//刪除尾部
typedef C::iterator it=c.begin();it++;
c.insert(it,47);
c.insert(it,3,86);//插入3個86
c.insert(it,c2.bein(),c2.end())
c.erase(it)
c.erase(it,it2)//清除序列中間的元素
c.swap(c2)//兩個容器交換所有
c.clear()//刪除容器中全部內容
6、向量:vector 索引和迭代操作速度快,除了在最后一個元素之后插入新元素外,向vector中插入一個對象是不可接受的操作。
? ? 副作用:當一個vector與存儲空間用完以后,為維護其連續的對象數組,它必須在另一個地方重新分配大塊新的存儲空間,并把以前已有的對象拷貝到新的 存儲空間中去。
? ? 已分配存儲區溢出的代價:1-分配一塊新的更大的存儲區。2-將舊存儲區中的對象拷貝到新開辟的存儲區中去,使用拷貝構造函數。3-銷毀舊存儲區中的所有對象。4-釋放舊存儲區的內存。
? ? 如果vector經常被填滿,系統將會為這些拷貝構造函數和析構函數操作的完成付出高昂的代價。
? ? 為了支持排序和查找等操作,需要有運算符operator< 、 operator==。int size=atoi("1000");<stdlib>
? ? 使用vector最有效的條件是:1-在開始時使用reserve()分配了正確數量的存儲區。因此vector不再重新分配存儲區。2-僅僅在序列的后端添加或刪除元素。利用迭代器向vector中間插入或者刪除元素是可能的。
7、雙端隊列:deque,在序列兩端對元素添加和刪除。允許快速隨機訪問。支持operator[]
? ? deque允許在序列兩端快速地插入和刪除元素,并且在其擴展存儲區的時候不需要拷貝和銷毀對象的操作。
? ? 1-deque沒有像vector的那種把所有東西保存在一塊連續的內存中的約束。利用多個連續存儲塊。2-不需要在分配新的存儲區是復制并銷毀所包含的對象。3-deque有push_front(),pop_front().
<ctime>: clock_t ticks=clock(); ticks=clock()-ticks;cout<<ticks<<endl;
添加行號:
for(size_t i=0;i<v.size();i++)
{
ostringstream ss;
ss<<i;
v[i]=ss.str()+":"+v[i];
}
vector、deque都提供隨機訪問,使用索引操作符operator[],使用at(),越界則拋出異常。使用at()代價更高一些。
8、鏈表: list:雙向鏈表數據結構。在任何地方快速插入或刪除元素。
list<string> l,l2;
l.reverse()//倒置
l.sort()//排序
swap(*it1,*it2)
reverse(l.begin(),l.end());//起作用
通用的sort()算法僅僅適用于數組、vector和deque
list<string>::iterator it1,it2;
it1=l.begin();
l.splice(it1,l2)//鏈表l在it1處開始結合鏈表l2,注意:結合之后l2是空的。
l.splice(it1,l2,it2)//鏈表l在it1處開始結合鏈表l2[it2]
l.splice(it1,l2,it2,it3)//鏈表l在it1處開始結合鏈表l2從it2開始終止在it3.
l2.remove(l2.back());//刪除具有特定值的所有元素
l2.remove(l2.front());
?l.merge(l2);//合并成倆個表,
?l.sort();
?l.unique();//從list中刪除所有相鄰的重復的對象,唯一的條件就是首先對list進行排序。
l.swap(l2)
9、集合:set僅接受每個元素的一個副本,并對元素排序。set是用一個平衡樹數據結構來存儲其元素以提供快速的查找。
set<string> s;
inserter(s,s.begin());
char c; bool isalpha(c);
string word;
s.insert(word)
insert_iterator<string> ii(word,word.begin())//從何處開始插入
while(p!=end)
{
*ii++=*p++:
}
s.insert(word);
ifstream in(fname);
istreambuf_iterator<char>p(in),end//這個迭代器從流中逐字符地 提取信息。
while (p!=end)
10、堆棧。stack,與queue、priority_queue一起被歸類為適配器。建立在另一個序列容器的基礎之上。它們將通過調整某一個序列容器以存儲自己的數據。
stack<string> Stack1;//默認使用deque
stack<string, vector<string> > Stack2
stack<string, list<string> > Stack3
string line;
Stack1.push(line+"\n");
while(!Stack1.empty()){
Stack1.top();
Stack1.pop();
}
可以使用一個vector及其back(),push_back(),pop_back()獲得等價的堆棧功能,還擁有vector 的附加功能。
11、隊列 queue,容器默認的模板參數是deque.
queue<string> que;
if(!que.empty())
{
cout<<que.front();
que.pop();
}
12、優先隊列 priority_queue #include <queue>
當向一個優先隊列中用push壓入一個對象時,那個對象根據一個比較函數或函數對象在隊列中排序。priority_queue確定在用top查看頂部元素時,該元素將是具有最高優先級的一個元素。處理完該元素,用pop刪除。
priority_queue<int> pqi;//
pqi.push(i);
while(!pqi.empty())
{
cout<<pqi.top();
pqi.pop();
}
改變優先級
priority_queue<int,vector<int>,greater<int> > pqi;//
0 0 0 0 1 1 2 2 3 3 3 3 3
1 11 11 11 11 12 12 12 12
17 17 17 17 17 18 18 18 1
?23 23 23 24 24 24 24 24
priority_queue<int,vector<int>,less<int> > pqi;//===priority_queue<int > pqi;//
24 24 23 23 23 23 2
?18 18 17 17 17 17
1 11 11 10 10 10 10
2 2 1 1 1 1 1 1 1 0
堆就是一個優先隊列:make_heap(),push_heap(),pop_heap(),priority_queue只是對它的封裝。使用sort_heap之后,再使用make_heap,轉換成堆。
13、持有二進制位:二進制位集合bitset和邏輯向量vector<bool>不同點:1-bitset持有固定二進制位,vector<bool>動態擴展。2-bitset沒有迭代器,vector<bool>是vector的特化。3-bitset與STL不相似,vector<bool>類似于類STL 容器。
bitset參數表示二進制位的個數:bitset<10>與bitset<20>是兩種不同的類型,不能將它們兩個之間進行比較、賦值等操作。從bitset到一個數的轉換用to_ulong()
bitset:
#include <bitset>
typedef bitset<32> BS;
BS a(rand());
unsigned long ul=a.to_ulong();
string bits("01111110");
BS a(bits);
BS b("11111110");
cout<<BS(bits)<<endl;//顯示一個完整string,不夠在前補0至32位,多了則刪除后面的。
cout<<BS(bits,2)從第二個字符開始,不夠在前補0至32位,多了則刪除后面的。
cout<<BS(bits,2,11)從第二個字符開始連續11個字符,不夠在前補0至32位,多了則刪除后面的。
cout<<a<<endl;//輸出二進制a
cout<<(a&b)<<endl;//00000000000000000000000001111110
cout<<(BS(a)&=b)
cout<<(a|b)<<endl;
cout<<(BS(a)|=b)
cout<<(a^b)<<endl;
cout<<(BS(a)^=b)
cout<<(BS(a)<<=16)
cout<<(BS(a)>>=16)
cout<<BS(a).set()//11111111111111111111111111111111
a.test(0)==0, a.test(1)==1//從右往左下標:76543210
cout<<BS(a).set(i)
cout<<BS(a).reset();//00000000000000000000000000000000
cout<BS(a).flip()//按位取反
cout<BS(a).flip(0)//按位取反第0位。
cout<<a.count()//1的個數
cout<<a.any()//有1存在?
cout<<a.none()//沒有1存在?
cout<<a.size()//BS 的二進制位數
vector<bool>:是vector模板的特化,
沒有set,reset,只有在vector基礎上加了flip
?ostringstream os;
?copy(vb.begin(), vb.end(),ostream_iterator<bool>(os, ""));
?bitset<10> bs(os.str());
?cout << "Bitset:" << endl << bs << endl;
14、關聯式容器 set/map/multiset/multimap 它們將關鍵字與值關聯起來。set可以看成是沒有值的map,它只有關鍵字。
關聯式容器最重要的操作是將對象放進容器。在set的情況下要查看該對象是否已在集合中,在map情況下先查看關鍵字是否已在map中,如果存在,就為關鍵字設置關聯值。
set<Noisy> s(a,a+SZ);
Noisy n;
s.insert(n);
cout<<s.count(n);//關鍵字出現的次數:0/1
if(s.find(n)!=s.end())
map<int , Noisy> nm;
for(i=0;i<10;i++){
nm[i];//自動創建對象,如果使用operator[]查找一個值而它又不存在的話,這個map就會創建一個新的關鍵字-值對。即:如果實際上想要查詢某個對象并不想創建一個新條目,就必須使用個成員函數count()或者find().
}
nm.insert(make_pair(47.n))
nm.count(10);//
map<int ,Notify>::iterator it=nm.find(6);//關鍵字出現的次數:0/1
cout<<(*it).first<<(*it).second;
typedef pair<const Key, T> value_type
set<int>s;
fill_n(inserter(s,s.begin()),10,7);
map<int,int>m
fill_n(inserter(m,m.begin()),10,make_pair(90,120))
copy(m.begin(),m.end(),ostream_iterator<pair<int,int> >(cout,"\n"));
multiset:可以插入每個值的多個對象所有相鄰的元素必須毗鄰存放。
清除容器的指針:
Container shapes;
shapes.push_back(new Circle);
...
for(it=shapes.begin(),it!=shapes.end();it++)
{
delete *it;
*it=0;
}
#ifndef PURGE_H
#define PURGE_H
#include <algorithm>
template<class Seq> void purge(Seq& c) {
? typename Seq::iterator i;
? for(i = c.begin(); i != c.end(); ++i) {
? ? delete *i;
? ? *i = 0;
? }
}
// Iterator version:
template<class InpIt> void purge(InpIt begin, InpIt end) {
? while(begin != end) {
? ? delete *begin;
? ? *begin = 0;
? ? ++begin;
? }
}
#endif // PURGE_H ///:~
第八章
1、通過指針或引用來決定對象運行時類型的一種方法是使用運行時類型轉換。此方法適合把基類指針類型轉換為派生類型,也稱向下類型轉換。
class Bond : public Security {
? typedef Security Super;
protected:
? enum { OFFSET = 2, TYPEID = BASEID + OFFSET };
public:
? bool isA(int id) {
? ? return id == TYPEID || Super::isA(id);
? }
? static Bond* dynacast(Security* s) {
? ? return (s->isA(TYPEID)) ? static_cast<Bond*>(s) : 0;
? }
};
dynamic_cast提供類型轉換檢查。
class Security {
public:
? virtual ~Security() {}
};
class Stock : public Security {};
class Bond : public Security {};
class Investment : public Security {
public:
? void special() {
? ? std::cout << "special Investment function" <<std::endl;
? }
};
class Metal : public Investment {};
使用指針:
vector<Security*> portfolio;
? portfolio.push_back(new Metal);
? portfolio.push_back(new Investment);
? portfolio.push_back(new Bond);
? portfolio.push_back(new Stock);
? for(vector<Security*>::iterator it =
? ? ? ?portfolio.begin();
? ? ? ?it != portfolio.end(); ++it) {
? ? Investment* cm = dynamic_cast<Investment*>(*it);
? ? if(cm)
? ? ? cm->special();
? ? else
? ? ? cout << "not a Investment" << endl;
? }
? cout << "cast from intermediate pointer:" << endl;
? Security* sp = new Metal;
? Investment* cp = dynamic_cast<Investment*>(sp);
? if(cp) cout << " ?it's an Investment" << endl;
? Metal* mp = dynamic_cast<Metal*>(sp);
? if(mp) cout << " ?it's a Metal too!" << endl;
dynamic_cast<目標類型>(操作數),dynamic_cast要求使用的目標對象的類型是多態的,這就要求該類必須至少有一個虛函數。轉換失敗拋出bad_cast ? <typeinfo>異常。
使用引用:
Metal m;
? Security& s = m;
? try {
? ? Investment& c = dynamic_cast<Investment&>(s);
? ? cout << "It's an Investment" << endl;
? } catch(bad_cast&) {
? ? cout << "s is not an Investment type" << endl;
? }
? try {
? ? Bond& b = dynamic_cast<Bond&>(s);
? ? cout << "It's a Bond" << endl;
? } catch(bad_cast&) {
? ? cout << "It's not a Bond type" << endl;
? }
2、type_info ::name(); typeid 返回type_info 類的對象,typeid獲得動態類型的名稱。const PolyBase *ppdtypeid(*ppb)
3、類型轉換到中間層次類型
4、typeid不能與void型指針一起工作。void *v=new Security;//!cout<<typeid(*v).name();//!Security *s=dynamic_cast<Security*>(v);
5、帶模板的RTTI:
#include <iostream>
#include <typeinfo>
using namespace std;
template<int id> class Announce {
public:
? Announce() {
? ? cout << typeid(*this).name() << " constructor" << endl;
? }
? ~Announce() {
? ? cout << typeid(*this).name() << " destructor" << endl;
? }
};
class X : public Announce<0> {
? Announce<1> m1;
? Announce<2> m2;
public:
? X() { cout << "X::X()" << endl; }
? ~X() { cout << "X::~X()" << endl; }
};
int main() { X x; } ///:~
//output
class Announce<0> constructor
class Announce<1> constructor
class Announce<2> constructor
X::X()
X::~X()
class Announce<2> destructor
class Announce<1> destructor
class Announce<0> destructor
第九章
1、接口繼承:僅僅在一個派生類接口中加入了成員函數的聲明。除了析構函數以外,這些聲明都是純虛函數。
class Printable {
public:
? virtual ~Printable() {}
? virtual void print(ostream&) const = 0;
};
class Intable {
public:
? virtual ~Intable() {}
? virtual int toInt() const = 0;
};
class Stringable {
public:
? virtual ~Stringable() {}
? virtual string toString() const = 0;
};
class Able : public Printable, public Intable,
public Stringable {
int myData;
public:
Able(int x) { myData = x; }
void print(ostream& os) const { os << myData; }
int toInt() const { return myData; }
string toString() const {
ostringstream os;
os << myData;
return os.str();
}
};
2、實現繼承:在派生類中實現所有的 細節。
protected類型需要一個友元或派生類來使用它?;惖奈鰳嫼瘮凳翘摵瘮???梢员WC派生類的對象正確的銷毀。
3、重復子對象
當從某個基類繼承時,可以在派生類中得到那個基類的所有數據成員的副本。
class A { int x; };
class B { int y; };
class C : public A, public B { int z; };
int main() {
? cout << "sizeof(A) == " << sizeof(A) << endl;
? cout << "sizeof(B) == " << sizeof(B) << endl;
? cout << "sizeof(C) == " << sizeof(C) << endl;
? C c;
? cout << "&c == " << &c << endl;
? A* ap = &c;
? B* bp = &c;
? cout << "ap == " << static_cast<void*>(ap) << endl;
? cout << "bp == " << static_cast<void*>(bp) << endl;
? C* cp = static_cast<C*>(bp);
? cout << "cp == " << static_cast<void*>(cp) << endl;
? cout << "bp == cp? " << boolalpha << (bp == cp) << endl;
? cp = 0;
? bp = cp;
? cout << bp << endl;
}
/* Output:
sizeof(A) == 4
sizeof(B) == 4
sizeof(C) == 12
&c == 1245052
ap == 1245052
bp == 1245056
cp == 1245052
bp == cp? true
0
*/ ///:~
如果有多個基類,若果這些基類依次有一個共同的基類,那么將得到頂層基類的兩個副本,
如果有多個基類,派生類向上類型轉換會出現二義性。
class Top {
? int x;
public:
? Top(int n) { x = n; }
};
class Left : public Top {
? int y;
public:
? Left(int m, int n) : Top(m) { y = n; }
};
class Right : public Top {
? int z;
public:
? Right(int m, int n) : Top(m) { z = n; }
};
class Bottom : public Left, public Right {
? int w;
public:
? Bottom(int i, int j, int k, int m)
? : Left(i, k), Right(j, k) { w = m; }
};
int main() {
? Bottom b(1, 2, 3, 4);
? cout << sizeof b << endl; // 20
} ///:~
4、虛基類:消除向上類型轉換時二義性的問題。
class Top {
protected:
? int x;
public:
? Top(int n) { x = n; }
? virtual ~Top() {}
? friend ostream&
? operator<<(ostream& os, const Top& t) {
? ? return os << t.x;
? }
};
class Left : virtual public Top {
protected:
? int y;
public:
? Left(int m, int n) : Top(m) { y = n; }
};
class Right : virtual public Top {
protected:
? int z;
public:
? Right(int m, int n) : Top(m) { z = n; }
};
class Bottom : public Left, public Right {
? int w;
public:
Bottom(int i, int j, int k, int m)
: Top(i), Left(0, j), Right(0, k) { w = m; }
friend ostream&
operator<<(ostream& os, const Bottom& b) {
return os << b.x << ',' << b.y << ',' << b.z
<< ',' << b.w;
}
};
int main() {
Bottom b(1, 2, 3, 4);
cout << sizeof b << endl;
cout << b << endl;
cout << static_cast<void*>(&b) << endl;
Top* p = static_cast<Top*>(&b);
cout << *p << endl;
cout << static_cast<void*>(p) << endl;
cout << dynamic_cast<void*>(p) << endl;
} ///:~
//output
28
1,2,3,4
0012FF58
1
0012FF6C
dynamic_cast到void*的結果總是確定指向完整對象的地址。
5、含有虛基類的子對象的初始化順序
1-所有虛基類子對象,按照他們在了定義中出現的位置,從上到下,從左到右初始化。
2-然后非虛基類按通常順序初始化。
3-所有的成員對象按聲明的順序初始化。
4-完整的對象的構造函數執行。
class M {
public:
? M(const string& s) { cout << "M " << s << endl; }
};
class A {
? M m;
public:
? A(const string& s) : m("in A") {
? ? cout << "A " << s << endl;
? }
? virtual ~A() {}
};
class B {
? M m;
public:
? B(const string& s) : m("in B") ?{
? ? cout << "B " << s << endl;
? }
? virtual ~B() {}
};
class C {
? M m;
public:
? C(const string& s) : m("in C") ?{
? ? cout << "C " << s << endl;
? }
? virtual ~C() {}
};
class D {
? M m;
public:
? D(const string& s) : m("in D") {
? ? cout << "D " << s << endl;
? }
? virtual ~D() {}
};
class E : public A, virtual public B, virtual public C {
? M m;
public:
? E(const string& s) : A("from E"), B("from E"),
? C("from E"), m("in E") {
? ? cout << "E " << s << endl;
? }
};
class F : virtual public B, virtual public C, public D {
M m;
public:
F(const string& s) : B("from F"), C("from F"),
D("from F"), m("in F") {
cout << "F " << s << endl;
}
};
class G : public E, public F {
M m;
public:
G(const string& s) : B("from G"), C("from G"),
E("from G"), ?F("from G"), m("in G") {
cout << "G " << s << endl;
}
};
int main() {
G g("from main");
} ///:~
//output
M in B
B from G
M in C
C from G
M in A
A from E
M in E
E from G
M in D
D from F
M in F
F from G
M in G
G from main
6、名字查找問題的二義性:
類繼承了兩個同名的函數,沒有方法在他們之間選擇:
lass Top {
public:
? virtual ~Top() {}
};
class Left : virtual public Top {
public:
? void f() {}
};
class Right : virtual public Top {
public:
? void f() {}
};
class Bottom : public Left, public Right {};
int main() {
? Bottom b;
? b.f(); // Error here
} ///:~
消除二義性調用的方法,是以基類名來限定函數的調用。
class Top {
public:
? virtual ~Top() {}
};
class Left : virtual public Top {
public:
? void f() {}
};
class Right : virtual public Top {
public:
? void f() {}
};
class Bottom : public Left, public Right {
public:
? using Left::f;
};
int main() {
? Bottom b;
? b.f(); // Calls Left::f()
} ///:~
名稱優勢:
class A {
public:
? virtual ~A() {}
? virtual void f() { cout << "A::f\n"; }
};
class B : virtual public A {
public:
? void f() { cout << "B::f\n"; }
};
class C : public B {};
class D : public C, virtual public A {};
int main() {
? B* p = new D;
? p->f(); // Calls B::f()
? delete p;
} ///:~
類A是類B的基類,名字B::f比名字A::f占優勢。
7、避免使用多繼承
關心兩個問題:1-是否需要通過新類來顯示兩個類的公共接口。2-需要向上類型轉換成為倆個類型嗎?
第十章 設計模式
動機:為了使變化的事物與不變的事物分離開。
1、模式分類
創建型:Creational:用于怎樣創建一個對象,隔離對象創建的細節,代碼不依賴于對象是什么類型,因此在增加一種新的對象類型時不需要改變代碼。Sington、Factory、Builder模式。
單件模式、工廠模式、構建器模式。
結構型:Structural:影響對象之間的連接方式,確保系統的變化不需要改變對象間的連接。Proxy、Adapter模式。代理模式和適配器模式。
行為型:Behavioral:在程序中處理具有特定操作類型的對象。這些對象封裝要執行的操作過程。如:解釋一種語言、實踐一個請求、遍歷一個序列、實現一個算法等。
Command、Template Method、State、Strategy、Chain of Responsibility、Observer、Multiple Dispatching、Vistor模式
命令模式、模板方法模式、狀態模式、策略模式、職責鏈模式、觀察者模式、多派遣模式、訪問者模式。
2、創建型:單件模式:Singleton ? 它是允許一個類有且僅有一個實例的方法。
#include <iostream>
using namespace std;
class Singleton {
? static Singleton s;
? int i;
? Singleton(int x) : i(x) { }
? Singleton& operator=(Singleton&); ?// Disallowed
? Singleton(const Singleton&); ? ? ? // Disallowed
public:
? static Singleton& instance() { return s; }
? int getValue() { return i; }
? void setValue(int x) { i = x; }
};
Singleton Singleton::s(47);
int main() {
? Singleton& s = Singleton::instance();
? cout << s.getValue() << endl;
? Singleton& s2=Singleton::instance();
? cout<<s2.getValue()<<endl;
? cout<<s.getValue()<<endl;
? s.setValue(10);
? cout<<s2.getValue()<<endl;
? cout<<s.getValue()<<endl;
? s2.setValue(11);
? cout<<s2.getValue()<<endl;
? cout<<s.getValue()<<endl;
} ///:~
//output:
47
47
47
10
10
11
11
1-創建單件模式的關鍵是防止客戶程序員獲得任何控制其對象生存期的權利。聲明所有構造函數為私有??截悩嬙旌瘮岛唾x值函數被聲明為私有。
2-可以采用靜態創建對象,也可以等待,直到客戶程序員提出要求再根據要求進行創建。
3-返回引用而不是返回指針,防止用戶不小心刪除指針。
4-任何情況下,對象應該私有保存。
單件的變體:將在一個成員函數內部的靜態對象的創建與單件類結合在一起。
一個類中的任何static靜態成員對象都表示一個單件:有且僅有一個對象被創建。
如果一個靜態對象依賴于另一個對象,靜態對象的初始化順序是很重要的。
在函數中定義一個靜態對象來控制初始化順序,直到該函數第一次被調用時才進行初始化。如果函數返回一個靜態對象的引用,就可以達到單件的效果。
上面程序修改如下:
#include <iostream>
using namespace std;
class Singleton {
int i;
Singleton(int x) : i(x) { }
Singleton& operator=(Singleton&); ?// Disallowed
Singleton(const Singleton&); ? ? ? // Disallowed
public:
static Singleton& instance() {?
static Singleton ?s(47);
return s;
}
int getValue() { return i; }
void setValue(int x) { i = x; }
};
int main() {
Singleton& s = Singleton::instance();
cout << s.getValue() << endl;
Singleton& s2=Singleton::instance();
cout<<s2.getValue()<<endl;
cout<<s.getValue()<<endl;
s.setValue(10);
cout<<s2.getValue()<<endl;
cout<<s.getValue()<<endl;
s2.setValue(11);
cout<<s2.getValue()<<endl;
cout<<s.getValue()<<endl;
} ///:~
兩個單件彼此依賴:可以很好地控制初始化過程。
class Singleton1{
Singleton1(){cout<<"Singleton1()"<<endl;}
public:
static Singleton1& ref(){
cout<<"前:static Singleton1& ref()"<<endl;
static Singleton1 single;
cout<<"后:static Singleton1& ref()"<<endl;
return single;
}
~Singleton1(){cout<<"~Singleton1()"<<endl;}
};
class Singleton2{
Singleton1 &s1;
Singleton2(Singleton1& s):s1(s){cout<<"Singleton2(Singleton1& s):s1(s)"<<endl;}
public:
static Singleton2& ref(){
cout<<"前:static Singleton2& ref()"<<endl;
static Singleton2 single(Singleton1::ref());
cout<<"后:static Singleton2& ref()"<<endl;
return single;
}
Singleton1 &f(){
cout<<"Singleton1 &f()"<<endl;
return s1;
}
~Singleton2(){cout<<"~Singleton2()"<<endl;}
};
int main(){
Singleton1 &s1=Singleton2::ref().f();
}
//output:
前:static Singleton2& ref()
前:static Singleton1& ref()
Singleton1()
后:static Singleton1& ref()
Singleton2(Singleton1& s):s1(s)
后:static Singleton2& ref()
Singleton1 &f()
~Singleton2()
~Singleton1()
單件的另一種變體:單件角:
MyClass通過下面3個步驟產生一個單件:1-聲明其構造函數為私有或保護的。2-聲明類Singleton<MyClass>為友元。3-從SIngleton<MyClass>派生出MyClass.步驟3:這是對模板Singleton中模板參數的靜態依賴。類Singleton<MyClass>能被編譯器實例化,因為它不依賴MyClass的大小。
#include <iostream>
using namespace std;
template<class T> class Singleton {
? Singleton(const Singleton&);
? Singleton& operator=(const Singleton&);
protected:
? Singleton() {}
? virtual ~Singleton() {}
public:
? static T& instance() {
? ? static T theInstance;
? ? return theInstance;
? }
};
// A sample class to be made into a Singleton
class MyClass : public Singleton<MyClass> {
? int x;
protected:
? friend class Singleton<MyClass>;
? MyClass() { x = 0; }
public:
? void setValue(int n) { x = n; }
? int getValue() const { return x; }
};
int main() {
? MyClass& m = MyClass::instance();
? cout << m.getValue() << endl;
? m.setValue(1);
? cout << m.getValue() << endl;
} ///:~
3、行為型:命令模式(Command):選擇操作
特點:消除代碼間的耦合,消除被調用函數的選擇與那個函數被調用位置之間的聯系。
主要特點:允許向一個函數或者對象傳遞一個想要的動作。
命令模式就是一個函數對象:一個作為對象的函數,通過將函數封裝為對象,就能夠以參數的形式傳遞給其他函數或者對象。
class Command {
public:
? virtual void execute() = 0;
};
class Hello : public Command {
public:
? void execute() { cout << "Hello "; }
};
class World : public Command {
public:
? void execute() { cout << "World! "; }
};
class IAm : public Command {
public:
? void execute() { cout << "I'm the command pattern!"; }
};
// An object that holds commands:
class Macro {
? vector<Command*> commands;
public:
? void add(Command* c) { commands.push_back(c); }
? void run() {
? ? vector<Command*>::iterator it = commands.begin();
? ? while(it != commands.end())
? ? ? (*it++)->execute();
? }
};
int main() {
Macro macro;
macro.add(new Hello);
macro.add(new World);
macro.add(new IAm);
macro.run();
} ///:~
4、結構型:代理模式(Proxy):作為其他對象的前端
代理模式和狀態模式都提供一個代理類,代碼與代理類打交道,而作實際工作的類隱藏在代理類背后。當調用代理類中的一個函數時,代理類轉而去調用實現類中相應的函數。從結構上看,代理模式是狀態模式的一個特例。
基本思想:代理類派生自一個基類,由平行地派生自同一個基類的一個或多個類提供實際的實現。
當一個代理對象被創建的時候,一個實現對象就分配給了它,代理對象就將函數調用發給實現對象。
從結構上看,代理模式和狀態模式的區別:代理模式只有一個實現類,而狀態模式有多個實現。應用也不同:代理模式控制對其實現類的訪問。而狀態模式動態的改變其實現類。
#include <iostream>
using namespace std;
class ProxyBase {
public:
? virtual void f() = 0;
? virtual void g() = 0;
? virtual void h() = 0;
? virtual ~ProxyBase() {}
};
class Implementation : public ProxyBase {
public:
? void f() { cout << "Implementation.f()" << endl; }
? void g() { cout << "Implementation.g()" << endl; }
? void h() { cout << "Implementation.h()" << endl; }
};
class Proxy : public ProxyBase {
? ProxyBase* implementation;
public:
? Proxy() { implementation = new Implementation(); }
? ~Proxy() { delete implementation; }
? // Forward calls to the implementation:
? void f() { implementation->f(); }
? void g() { implementation->g(); }
? void h() { implementation->h(); }
};
int main() ?{
? Proxy p;
? p.f();
? p.g();
? p.h();
} ///:~
5、行為型:狀態模式(State):改變對象的行為
狀態模式產生一個改變其類的對象,當發現在大多數或者所有函數中都存在有條件的代碼時,這種模式很有用。和代理模式一樣,狀態模式通過一個前端對象來使用后端實現對象。
測試一個bool變量。
class Creature {
bool isFrog;
public:
Creature() : isFrog(true) {}
void greet() {
if(isFrog)
cout << "Ribbet!" << endl;
else
cout << "Darling!" << endl;
}
void kiss() { isFrog = false; }
};
在所有操作前都必須測試isFrog變量,使用狀態對象,是代碼簡化。
#include <iostream>
#include <string>
using namespace std;
class Creature {
class State {
public:
virtual string response() = 0;
};
class Frog : public State {
public:
string response() { return "Ribbet!"; }
};
class Prince : public State {
public:
string response() { return "Darling!"; }
};
State* state;
public:
Creature() : state(new Frog()) {}
void greet() {
cout << state->response() << endl;
}
void kiss() {
delete state;
state = new Prince();
}
};
int main() {
Creature creature;
creature.greet();
creature.kiss();
creature.greet();
} ///:~
6、結構型:適配器模式(Adapter)
接受一種類型并且提供一個對其他類型的接口。
創建適配器,接受FibonacciGenerator并產生一個供STL算法使用的迭代器。
class FibonacciGenerator {//斐波那契數列
? int n;
? int val[2];
public:
? FibonacciGenerator() : n(0) { val[0] = val[1] = 0; }
? int operator()() {
? ? int result = n > 2 ? val[0] + val[1] : n > 0 ? 1 : 0;
? ? ++n;
? ? val[0] = val[1];
? ? val[1] = result;
? ? return result;
? }
? int count() { return n; }
};
#include <iostream>
#include <algorithm>
#include <numeric>
#include "FibonacciGenerator.h"
using namespace std;
class FibonacciAdapter { // Produce an iterator
? FibonacciGenerator f;
? int length;
public:
? FibonacciAdapter(int size) : length(size) {}
? class iterator;
? friend class iterator;
? class iterator : public std::iterator<
? ? std::input_iterator_tag, FibonacciAdapter, ptrdiff_t> {
? ? FibonacciAdapter& ap;
? public:
? ? typedef int value_type;
? ? iterator(FibonacciAdapter& a) : ap(a) {}
? ? bool operator==(const iterator&) const {
? ? ? return ap.f.count() == ap.length;
? ? }
? ? bool operator!=(const iterator& x) const {
? ? ? return !(*this == x);
? ? }
? ? int operator*() const { return ap.f(); }
? ? iterator& operator++() { return *this; }
? ? iterator operator++(int) { return *this; }
? };
? iterator begin() { return iterator(*this); }
? iterator end() { return iterator(*this); }
};
int main() {
? const int SZ = 20;
? FibonacciAdapter a1(SZ);
? cout << "accumulate: "
? ? << accumulate(a1.begin(), a1.end(), 0) << endl;
}
7、行為型:模板方法模式(Template Method)
模板方法模式的一個重要特征是它的定義在基類中,并且不能改動。模板方法模式就是堅持相同的代碼,它調用基類的不同函數來驅動程序運行。
驅動程序運行的引擎是模板方法模式,這個引擎是主要的事件環,客戶程序員只需提供customize1()、customize2()的定義,便可以令應用程序運行。
class ApplicationFramework {
protected:
? virtual void customize1() = 0;
? virtual void customize2() = 0;
public:
? void templateMethod() {
? ? for(int i = 0; i < 5; i++) {
? ? ? customize1();
? ? ? customize2();
? ? }
? }
};
// Create a new "application":
class MyApp : public ApplicationFramework {
protected:
? void customize1() { cout << "Hello "; }
? void customize2() { cout << "World!" << endl; }
};
int main() {
? MyApp app;
? app.templateMethod();
} ///:~
8、行為型:策略模式(Strategy):運行時選擇算法
將變化的代碼從“堅持相同代碼”中分開。策略即:使用多種方法來解決某個問題。
好處:在程序運行時可以加入變化的代碼。
class NameStrategy {
public:
? virtual void greet() = 0;
};
class SayHi : public NameStrategy {
public:
? void greet() {
? ? cout << "Hi! How's it going?" << endl;
? }
};
class Ignore : public NameStrategy {
public:
? void greet() {
? ? cout << "(Pretend I don't see you)" << endl;
? }
};
class Admission : public NameStrategy {
public:
? void greet() {
? ? cout << "I'm sorry. I forgot your name." << endl;
? }
};
// The "Context" controls the strategy:
class Context {
NameStrategy& strategy;
public:
Context(NameStrategy& strat) : strategy(strat) {}
void greet() { strategy.greet(); }
};
int main() {
SayHi sayhi;
Ignore ignore;
Admission admission;
Context c1(sayhi), c2(ignore), c3(admission);
c1.greet();
c2.greet();
c3.greet();
} ///:~
9、行為型:職責鏈模式(Chain of Responsibility):嘗試采用一系列策略模式,本質:嘗試多個解決方法直到找到一個起作用的方法。
職責鏈可看做是使用策略對象的遞歸。在職責鏈中,一個函數調用自身,調用函數的一個不同實現,如此反復直至達到某個終止條件,這個終止套件或者是已到達策略鏈的地底部或者是找到一個成功的策略。職責鏈實際上是一個鏈表,動態創建。
使用自動遞歸搜索鏈中每個策略的機制,職責鏈模式自動找到一個解決方法。
#ifndef PURGE_H
#define PURGE_H
#include <algorithm>
template<class Seq> void purge(Seq& c) {
? typename Seq::iterator i;
? for(i = c.begin(); i != c.end(); ++i) {
? ? delete *i;
? ? *i = 0;
? }
}
// Iterator version:
template<class InpIt> void purge(InpIt begin, InpIt end) {
? while(begin != end) {
? ? delete *begin;
? ? *begin = 0;
? ? ++begin;
? }
}
#endif // PURGE_H ///:~
#include <iostream>
#include <vector>
#include "purge.h"
using namespace std;
enum Answer { NO, YES };
class GimmeStrategy {
public:
? virtual Answer canIHave() = 0;
? virtual ~GimmeStrategy() {}
};
class AskMom : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Mooom? Can I have this?" << endl;
? ? return NO;
? }
};
class AskDad : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Dad, I really need this!" << endl;
? ? return NO;
? }
};
class AskGrandpa : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Grandpa, is it my birthday yet?" << endl;
? ? return NO;
? }
};
class AskGrandma : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Grandma, I really love you!" << endl;
? ? return YES;
? }
};
class Gimme : public GimmeStrategy {
vector<GimmeStrategy*> chain;
public:
Gimme() {
chain.push_back(new AskMom());
chain.push_back(new AskDad());
chain.push_back(new AskGrandpa());
chain.push_back(new AskGrandma());
}
Answer canIHave() {
vector<GimmeStrategy*>::iterator it = chain.begin();
while(it != chain.end())
if((*it++)->canIHave() == YES)
return YES;
// Reached end without success...
cout << "Whiiiiinnne!" << endl;
return NO;
}
~Gimme() { purge(chain); }
};
int main() {
Gimme chain;
chain.canIHave();
} ///:~
10、創建型:工廠模式(Factory):封裝對象的創建。
將創建對象的代碼轉到這個工廠中執行,那么在增加新對象時所做的全部工作就是只需要修改工廠。
實現工廠模式的一種方法就是在基類中定義一個靜態成員函數。
#include <iostream>
#include <stdexcept>
#include <cstddef>
#include <string>
#include <vector>
#include "purge.h"
using namespace std;
class Shape {
public:
virtual void draw() = 0;
virtual void erase() = 0;
virtual ~Shape() {}
class BadShapeCreation : public logic_error {
public:
BadShapeCreation(string type)
: logic_error("Cannot create type " + type) {}
};
static Shape* factory(const string& type)
throw(BadShapeCreation);
};
class Circle : public Shape {
Circle() {} // Private constructor
friend class Shape;
public:
void draw() { cout << "Circle::draw" << endl; }
void erase() { cout << "Circle::erase" << endl; }
~Circle() { cout << "Circle::~Circle" << endl; }
};
class Square : public Shape {
Square() {}
friend class Shape;
public:
void draw() { cout << "Square::draw" << endl; }
void erase() { cout << "Square::erase" << endl; }
~Square() { cout << "Square::~Square" << endl; }
};
Shape* Shape::factory(const string& type)
throw(Shape::BadShapeCreation) {
if(type == "Circle") return new Circle;
if(type == "Square") return new Square;
throw BadShapeCreation(type);
}
char* sl[] = { "Circle", "Square", "Square",
"Circle", "Circle", "Circle", "Square" };
int main() {
vector<Shape*> shapes;
try {
for(size_t i = 0; i < sizeof sl / sizeof sl[0]; i++)
shapes.push_back(Shape::factory(sl[i]));
} catch(Shape::BadShapeCreation e) {
cout << e.what() << endl;
purge(shapes);
return EXIT_FAILURE;
}
for(size_t i = 0; i < shapes.size(); i++) {
shapes[i]->draw();
shapes[i]->erase();
}
purge(shapes);
} ///:~
多態工廠:使用不同類的工廠派生自基本類型的工廠。
工廠模式是多態工廠的一個特例。
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <cstddef>
#include "purge.h"
using namespace std;
class Shape {
public:
? virtual void draw() = 0;
? virtual void erase() = 0;
? virtual ~Shape() {}
};
class ShapeFactory {
? virtual Shape* create() = 0;
? static map<string, ShapeFactory*> factories;
public:
? virtual ~ShapeFactory() {}
? friend class ShapeFactoryInitializer;
? static Shape* createShape(const string& id) {
? ? if(factories.find(id) != factories.end())
? ? ? return factories[id]->create();
? }
};
map<string, ShapeFactory*> ShapeFactory::factories;
class Circle : public Shape {
? Circle() {} // Private constructor
? friend class ShapeFactoryInitializer;
? class Factory;
? friend class Factory;
? class Factory : public ShapeFactory {
? public:
? ? Shape* create() { return new Circle; }
? ? friend class ShapeFactoryInitializer;
? };
public:
? void draw() { cout << "Circle::draw" << endl; }
? void erase() { cout << "Circle::erase" << endl; }
? ~Circle() { cout << "Circle::~Circle" << endl; }
};
class Square : public Shape {
? Square() {}
? friend class ShapeFactoryInitializer;
? class Factory;
? friend class Factory;
? class Factory : public ShapeFactory {
? public:
? ? Shape* create() { return new Square; }
? ? friend class ShapeFactoryInitializer;
? };
public:
? void draw() { cout << "Square::draw" << endl; }
? void erase() { cout << "Square::erase" << endl; }
? ~Square() { cout << "Square::~Square" << endl; }
};
// Singleton to initialize the ShapeFactory:
class ShapeFactoryInitializer {
? static ShapeFactoryInitializer si;
? ShapeFactoryInitializer() {
? ? ShapeFactory::factories["Circle"]= new Circle::Factory;
? ? ShapeFactory::factories["Square"]= new Square::Factory;
? }
public:
~ShapeFactoryInitializer() {
? ? map<string, ShapeFactory*>::iterator it =
? ? ? ShapeFactory::factories.begin();
? ? while(it != ShapeFactory::factories.end())
? ? ? delete it++->second;
? }
};
ShapeFactoryInitializer ShapeFactoryInitializer::si;
char* sl[] = { "Circle", "Square", "Square",
? "Circle", "Circle", "Circle", "Square" };
int main() {
? vector<Shape*> shapes;
? ? for(size_t i = 0; i < sizeof sl / sizeof sl[0]; i++)
? ? ? shapes.push_back(ShapeFactory::createShape(sl[i]));
? for( i = 0; i < shapes.size(); i++) {
? ? shapes[i]->draw();
? ? shapes[i]->erase();
? }
? purge(shapes);
} ///:~
//output:
Circle::draw
Circle::erase
Square::draw
Square::erase
Square::draw
Square::erase
Circle::draw
Circle::erase
Circle::draw
Circle::erase
Circle::draw
Circle::erase
Square::draw
Square::erase
Circle::~Circle
Square::~Square
Square::~Square
Circle::~Circle
Circle::~Circle
Circle::~Circle
Square::~Square
抽象工廠:使用若干工廠方法模式,每個工廠方法模式創建一個不同類型的對象。
class Obstacle {
public:
? virtual void action() = 0;
};
class Player {
public:
? virtual void interactWith(Obstacle*) = 0;
};
class Kitty: public Player {
? virtual void interactWith(Obstacle* ob) {
? ? cout << "Kitty has encountered a ";
? ? ob->action();
? }
};
class KungFuGuy: public Player {
? virtual void interactWith(Obstacle* ob) {
? ? cout << "KungFuGuy now battles against a ";
? ? ob->action();
? }
};
class Puzzle: public Obstacle {
public:
? void action() { cout << "Puzzle" << endl; }
};
class NastyWeapon: public Obstacle {
public:
? void action() { cout << "NastyWeapon" << endl; }
};
// The abstract factory:
class GameElementFactory {
public:
? virtual Player* makePlayer() = 0;
? virtual Obstacle* makeObstacle() = 0;
};
// Concrete factories:
class KittiesAndPuzzles : public GameElementFactory {
public:
? virtual Player* makePlayer() { return new Kitty; }
? virtual Obstacle* makeObstacle() { return new Puzzle; }
};
class KillAndDismember : public GameElementFactory {
public:
? virtual Player* makePlayer() { return new KungFuGuy; }
? virtual Obstacle* makeObstacle() {
? ? return new NastyWeapon;
? }
};
class GameEnvironment {
? GameElementFactory* gef;
? Player* p;
? Obstacle* ob;
public:
? GameEnvironment(GameElementFactory* factory)
? : gef(factory), p(factory->makePlayer()),
? ? ob(factory->makeObstacle()) {}
? void play() { p->interactWith(ob); }
? ~GameEnvironment() {
? ? delete p;
? ? delete ob;
? ? delete gef;
? }
};
int main() {
? GameEnvironment
? ? g1(new KittiesAndPuzzles),
? ? g2(new KillAndDismember);
? g1.play();
? g2.play();
}
/* Output:
Kitty has encountered a Puzzle
KungFuGuy now battles against a NastyWeapon */ ///:~
虛函數的思想就是發送一個消息給對象,讓對象確定要做正確的事情。
11、創建型:構建器模式(Builder)
目標:將對象的創建與它的表示法分開。構建器模式和抽象工廠模式主要的區別就是,構建器模式一步步創建對象,
功能:將部件組合成為一個完整的產品的算法和部件本身分開,這樣就允許通過一個共同借款的不同實現來為不同的產品提供不同的算法。
12、行為型:觀察者模式(Observer)
解決一個常見問題:當某些其他對象改變狀態時,應該如何處理。
在觀察著模式中有兩個變化的事件,正在進行觀察的對象的數量和更新發生的方式。即觀察著模式允許修改這二者而不影響周圍的其他代碼。
#ifndef OBSERVER_H
#define OBSERVER_H
class Observable;
class Argument {};
class Observer {
public:
? // Called by the observed object, whenever
? // the observed object is changed:
? virtual void update(Observable* o, Argument* arg) = 0;
? virtual ~Observer() {}
};
#endif // OBSERVER_H ///:~
類Obervable只有一個成員函數:update()接口類。當正在被觀察的對象認為到了更新其所有觀察者的時機時,它將調用此函數。它允許被觀察者的對象傳遞引起更新操作的對象和任何額外的信息。
#ifndef OBSERVABLE_H
#define OBSERVABLE_H
#include <set>
#include "Observer.h"
class Observable {
? bool changed;
? std::set<Observer*> observers;
protected:
? virtual void setChanged() { changed = true; }
? virtual void clearChanged() { changed = false; }
public:
? virtual void addObserver(Observer& o) {
? ? observers.insert(&o);
? }
? virtual void deleteObserver(Observer& o) {
? ? observers.erase(&o);
? }
? virtual void deleteObservers() {
? ? observers.clear();
? }
? virtual int countObservers() {
? ? return observers.size();
? }
? virtual bool hasChanged() { return changed; }
? // If this object has changed, notify all
? // of its observers:
? virtual void notifyObservers(Argument* arg = 0) {
? ? if(!hasChanged()) return;
? ? clearChanged(); // Not "changed" anymore
? ? std::set<Observer*>::iterator it;
? ? for(it = observers.begin();it != observers.end(); it++)
? ? ? (*it)->update(this, arg);
? }
? virtual ~Observable() {}
};
#endif // OBSERVABLE_H ///:~
13、行為型:多重派遣模式(Multiple dispatching)
好處:在調用的時候能夠以簡潔的句法表達方式達到預期的效果。
多態只能通過虛函數調用來實現,要發生多重派遣,必須有一個虛函數調用以確定每個未知的類型。
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <ctime>
#include <cstdlib>
#include "purge.h"
using namespace std;
class Paper;
class Scissors;
class Rock;
enum Outcome { WIN, LOSE, DRAW };
ostream& operator<<(ostream& os, const Outcome out) {
? switch(out) {
? ? default:
? ? case WIN: return os << "win";
? ? case LOSE: return os << "lose";
? ? case DRAW: return os << "draw";
? }
}
class Item {
public:
? virtual Outcome compete(const Item*) = 0;
? virtual Outcome eval(const Paper*) const = 0;
? virtual Outcome eval(const Scissors*) const= 0;
? virtual Outcome eval(const Rock*) const = 0;
? virtual ostream& print(ostream& os) const = 0;
? virtual ~Item() {}
? friend ostream& operator<<(ostream& os, const Item* it) {
? ? return it->print(os);
? }
};
class Paper : public Item {
public:
? Outcome compete(const Item* it) { return it->eval(this);}
? Outcome eval(const Paper*) const { return DRAW; }
? Outcome eval(const Scissors*) const { return WIN; }
? Outcome eval(const Rock*) const { return LOSE; }
? ostream& print(ostream& os) const {
? ? return os << "Paper ? ";
? }
};
class Scissors : public Item {
public:
? Outcome compete(const Item* it) { return it->eval(this);}
? Outcome eval(const Paper*) const { return LOSE; }
? Outcome eval(const Scissors*) const { return DRAW; }
? Outcome eval(const Rock*) const { return WIN; }
? ostream& print(ostream& os) const {
? ? return os << "Scissors";
? }
};
class Rock : public Item {
public:
? Outcome compete(const Item* it) { return it->eval(this);}
? Outcome eval(const Paper*) const { return WIN; }
? Outcome eval(const Scissors*) const { return LOSE; }
? Outcome eval(const Rock*) const { return DRAW; }
? ostream& print(ostream& os) const {
? ? return os << "Rock ? ?";
? }
};
struct ItemGen {
? Item* operator()() {
? ? switch(rand() % 3) {
? ? ? default:
? ? ? case 0: return new Scissors;
? ? ? case 1: return new Paper;
? ? ? case 2: return new Rock;
? ? }
? }
};
struct Compete {
? Outcome operator()(Item* a, Item* b) {
? ? cout << a << "\t" << b << "\t";
? ? return a->compete(b);
? }
};
int main() {
? srand(time(0)); // Seed the random number generator
? const int sz = 20;
? vector<Item*> v(sz*2);
? generate(v.begin(), v.end(), ItemGen());
? transform(v.begin(), v.begin() + sz,
? ? v.begin() + sz,
? ? ostream_iterator<Outcome>(cout, "\n"),
? ? Compete());
? purge(v);
} ///:~
//output:
003807A8 ? ? ? ?00382A10 ? ? ? ?1
003807E0 ? ? ? ?00382A48 ? ? ? ?0
00380818 ? ? ? ?00382A80 ? ? ? ?2
00382658 ? ? ? ?00382AB8 ? ? ? ?0
00382690 ? ? ? ?00382AF0 ? ? ? ?0
003826C8 ? ? ? ?00382B28 ? ? ? ?1
00382700 ? ? ? ?00382B60 ? ? ? ?1
00382738 ? ? ? ?00382B98 ? ? ? ?2
00382770 ? ? ? ?00382BD0 ? ? ? ?2
003827A8 ? ? ? ?00382C08 ? ? ? ?2
003827E0 ? ? ? ?00382C40 ? ? ? ?1
00382818 ? ? ? ?00382C78 ? ? ? ?0
00382850 ? ? ? ?00382CB0 ? ? ? ?1
00382888 ? ? ? ?00382CE8 ? ? ? ?0
003828C0 ? ? ? ?00382D20 ? ? ? ?2
003828F8 ? ? ? ?00382D58 ? ? ? ?2
00382930 ? ? ? ?00382D90 ? ? ? ?1
00382968 ? ? ? ?00382DC8 ? ? ? ?2
003829A0 ? ? ? ?00382E00 ? ? ? ?1
003829D8 ? ? ? ?00382E38 ? ? ? ?2
virtual Item::compete函數開始雙重派遣。函數compete調用eval執行第二次派遣。
14、行為型:訪問者模式(Visitor)
目標:將類繼承層次結構上的操作與這個層次結構本身分開。訪問者模式建立在雙重派遣方案之上。訪問者模式允許創建一個獨立的類層次結構Visitor而有效的對主類的借口進行擴展。
#include <algorithm>
#include <iostream>
#include <typeinfo>
#include <string>
#include <vector>
#include <ctime>
#include <cstdlib>
#include "purge.h"
using namespace std;
class Gladiolus;
class Renuculus;
class Chrysanthemum;
class Visitor {
public:
? virtual void visit(Gladiolus* f) = 0;
? virtual void visit(Renuculus* f) = 0;
? virtual void visit(Chrysanthemum* f) = 0;
? virtual ~Visitor() {}
};
class Flower {
public:
? virtual void accept(Visitor&) = 0;
? virtual ~Flower() {}
};
class Gladiolus : public Flower {
public:
? virtual void accept(Visitor& v) {
? ? v.visit(this);
? }
};
class Renuculus : public Flower {
public:
? virtual void accept(Visitor& v) {
? ? v.visit(this);
? }
};
class Chrysanthemum : public Flower {
public:
? virtual void accept(Visitor& v) {
? ? v.visit(this);
? }
};
// Add the ability to produce a string:
class StringVal : public Visitor {
? string s;
public:
? operator const string&() { return s; }
? virtual void visit(Gladiolus*) {
? ? s = "Gladiolus";
? }
? virtual void visit(Renuculus*) {
? ? s = "Renuculus";
? }
? virtual void visit(Chrysanthemum*) {
? ? s = "Chrysanthemum";
? }
};
// Add the ability to do "Bee" activities:
class Bee : public Visitor {
public:
? virtual void visit(Gladiolus*) {
? ? cout << "Bee and Gladiolus" << endl;
? }
? virtual void visit(Renuculus*) {
? ? cout << "Bee and Renuculus" << endl;
? }
? virtual void visit(Chrysanthemum*) {
? ? cout << "Bee and Chrysanthemum" << endl;
? }
};
struct FlowerGen {
? Flower* operator()() {
? ? switch(rand() % 3) {
? ? ? default:
? ? ? case 0:?
?cout<<"new Gladiolus"<<endl;
?return new Gladiolus;
? ? ? case 1:?
? cout<<"new Renuculus"<<endl;
? return new Renuculus;
? ? ? case 2:
? cout<<"new Chrysanthemum"<<endl;
? return new Chrysanthemum;
? ? }
? }
};
int main() {
? srand(time(0)); // Seed the random number generator
? vector<Flower*> v(10);
? generate(v.begin(), v.end(), FlowerGen());
? cout<<endl;
? vector<Flower*>::iterator it;
? // It's almost as if I added a virtual function
? // to produce a Flower string representation:
? StringVal sval;
? for(it = v.begin(); it != v.end(); it++) {
? ? (*it)->accept(sval);
? ? cout << " string(sval)"<<string(sval) << endl;
? }
? // Perform "Bee" operation on all Flowers:
? Bee bee;
? for(it = v.begin(); it != v.end(); it++)
?(*it)->accept(bee);
? purge(v);
} ///:~
//output:
new Chrysanthemum
new Chrysanthemum
new Renuculus
new Renuculus
new Gladiolus
new Gladiolus
new Gladiolus
new Renuculus
new Renuculus
new Chrysanthemum
?string(sval)Chrysanthemum
?string(sval)Chrysanthemum
?string(sval)Renuculus
?string(sval)Renuculus
?string(sval)Gladiolus
?string(sval)Gladiolus
?string(sval)Gladiolus
?string(sval)Renuculus
?string(sval)Renuculus
?string(sval)Chrysanthemum
Bee and Chrysanthemum
Bee and Chrysanthemum
Bee and Renuculus
Bee and Renuculus
Bee and Gladiolus
Bee and Gladiolus
Bee and Gladiolus
Bee and Renuculus
Bee and Renuculus
Bee and Chrysanthemum
Flower是主層次結構,Flower的各個子類通過函數accept()得到一個Visitor,Flower主層次結構除了函數accept沒有別的操作,因此Flower層次結構的所有功能都將包含在Visitor層次結構中。每個Flower中的accept函數開始一個雙重派遣。第一次派遣決定了Flower類型,第二次派遣決定了Visitor類型?
第十一章
1、進程是在其自己的地址空間運行的自含式程序。線程是進程內的單一連續的控制流。因此一個進程可以有多個并發執行的線程。線程運行在一個進程內,所以它們共享內存和其他資源。
2、防止兩個線程在臨界區訪問同一資源,加鎖,互斥。 在進入臨界區之前獲得互斥鎖,在臨界區的終點釋放。
3、原子操作:1-返回int型變量。2-原子操作不能被線程處理機制中斷。3-對int型變量增1操作也是原子操作。
4、線程狀態:1-新建狀態。2-可運行狀態。3-阻塞狀態。4-死亡狀態。
5、變為阻塞狀態:原因:1-調用sleep()使線程處于休眠狀態。2-已經使用wait()掛起了該線程的運行,在得到signal()或broadcast()消息之前,它不會變為可執行狀態。3-線程正在等待某個I/O操作完成。4-線程正在嘗試進入一段被一個互斥鎖保護的代碼塊。
6、除了I/O操作,一個任務可以從任何阻塞操作中中斷出來。
7、進程間協作,通過互斥鎖來同步兩個任務的行為,來阻止一個任務干擾另一個任務的資源。
8、死鎖發生的條件:1-互斥。線程使用的資源至少有一個必須是不可共享的。2-至少有一個進程必須持有某一種資源。并且同時等待獲得正在被另外的進程所持有的資源。3、不能以搶占的方式剝奪一個進程的資源。4、出現一個循環等待。一個進程等待另外的進程所持有的資源,而這個被等待的進程又等待另一個進程所持有的資源。。。
破壞死鎖的最容易的方法是破壞4.
9、5個哲學家就餐問題:每個哲學家以特定的順序拿筷子:先右后左。進入循環等待。解決方法:最后一個哲學家初始化為:先嘗試拿左邊的筷子,再拿右邊的筷子。
10、線程的優點:提供輕量級(100條指令)的執行語境切換,而非重量級(上千條指令)進程語境切換。進程中所有的線程共享同一內存空間。一個輕量級的語境切換只改變了程序執行的先后順序和局部變量。進程改變-重量級語境切換-必須調換所有內存空間。
第一章
1、異常處理是C++的主要特征之一
2、assert():用于開發階段調試,#define NDEBUG 使得assert()失效。
3、C語言中錯誤處理信息:1-在函數中返回錯誤信息。2-使用C庫的信號處理系統signal(),raise()。3-使用C庫的setjmp()和longjmp().信號處理方法和setjmp、longjmp函數不調用析構函數,對象不能被正確清理。
4、throw 創建程序所拋出對象的一個拷貝。throw表達式返回這個對象。try/catch異常處理器可以處理某種異常類型和這種異常類型的派生類。并且為避免再次拷貝異常對象,最好通過引用而不是通過值來捕獲異常。通過引用來捕獲異常,基類異常處理器能夠捕獲派生類異常。
5、catch(...){}可以捕獲任何類型的異常。在異常處理器內部catch(){throw;}使用不帶參數的throw可以重新拋出異常。
6、如果沒有一個異常處理器可以捕獲異常,terminate()會自動被調用。<exception>中定義.terminate調用abort(),導致程序不會正常終止函數,全局對象額靜態對象的析構函數不會執行。terminate也會在下面兩個條件下執行:局部對象的析構函數拋出異常時,棧正在進行清理工作;或者是全局對象或靜態對象的構造函數或析構函數拋出異常。set_terminate函數可以設置自己的terminate函數,自定義的terminator不能有參數,且返回值為void類型。第一次調用返回默認的terminate函數的指針,這樣可以在需要的時候恢復原來的terminate。
7、防止資源泄漏,采用方式:1-在構造函數中捕獲異常,用于釋放資源。2-在對象的構造函數中分配資源,并在對象的析構函數中釋放資源。
8、若返回類型為引用,則表示不能返回0.因為不能有空引用。
9、RAII,資源獲得式初始化,是一個封裝類,用于封裝指向分配的對內存的指針,使得程序能夠自動釋放這些內存。auto_ptr類模板在頭文件<memeroy>中定義。auto_ptr<類>
10、從exception類(定義在頭文件<exception>中)派生自己定義的標準異常類。兩個派生類:logic_error和runtime_error。這兩個類在頭文件<stdexcept>文件中,派生類接受參數std::string的構造函數。通過exception::what()函數,可以得到保存的消息。因為exception沒有參數為string的構造函數,所以用戶最好從兩個派生類派生自己定義的類。
11、異常規格說明:int fun () throw(big,small);函數可能拋出的異常寫在throw之后的括號中。int fun()throw()表示函數不會拋出任何異常。int fun()表示函數有可能拋出任何類型的異常。如果異常不在異常規格說明的異常集中,那么unexcepted會被調用。默認的unexpected調用terminate函數。可以使用set_unexpected函數(頭文件<exception>)使用一個函數指針作為參數,這個指針指向的函數沒有參數,而且其返回類型為void。set_unexpected函數返回unexpected先前的值,所以可以保存這個值,以后使用它。
12、異常規格說明中包含std::bad_exception 在<exception>中,則unexpected異常會被換成std::bad_exception對象,程序恢復到函數被調用的位置重新開始異常匹配。如果異常規格說明中不包含std::bad_exception,程序會調用terminate函數。VC++中不會調用set_unexpected()函數。
13、異常規格說明與繼承。派生類的異常說明應該在結構層次上低于基類的異常說明。即基類的異常說明--》派生出派生類的異常說明。異常規格說明是為非模板準備的。
14、operator=應遵守6個模式:1-確保程序不給自己賦值,如果是的話跳6。2-給指針數據成員分配新內存。3-從原有的內存區向新分配的內存區拷貝數據。4-釋放原有的內存。5-更新對象狀態,也就是把指向分配新堆內存地址的指針賦值給指針數據成員。6-返回*this。
15、當delete對象的時候,對象的析構函數會被調用。不要在析構函數中拋出異常 。
第二章
1、測試套件框架TestSuit,包含兩個主要的類:Test和Suit。Test類保存測試成功和失敗的次數??梢詮腡est類派生自己的測試對象。只需要重寫成員函數run()即可,并用test_()來測試。使用report函數顯示前面的輸出。
2、Test類有3個虛函數:虛析構函數、reset函數,純虛函數run;基類設置虛析構函數,表示可以通過基類指針釋放在堆上分配的派生類對象。
3、利用宏處理代碼跟蹤:這兩個宏可以完成調試器的基本功能,跟蹤代碼執行,并顯示表達式的值。
#define TRACE(ARG) cout<<#ARG<<endl; ARG ? 變種:#define TRACE(ARG) cout<<#ARG"=["<<ARG<<"]"<<endl;
第三章?
1、C++字符串string極大減少了C語言編程中3中最常見的錯誤:1-數組越界。2-通過未被初始化或被賦以錯誤值的指針來訪問數組元素。3-在釋放了某一數組原先所分配的存儲單元后仍保留了懸掛指針。
2、創建string對象:1-創建空string對象。2-將字符串傳給構造函數。3-用string=字符串方式初始化。4-用一個string初始化另一個string。
3、string s2(s1,0,8)從s1[0]開始的8個字符賦值給s2.
4、string s; s.substr(20,10)提取從s[20]開始的10個字符。
5、使用迭代器。string source; string s(source.begin(),source.end());
6、string s(5,'a'),用5個字符'a'初始化s.
7、對字符串操作:string s, 大小:s.size(),s.length(). 當前分配的存儲空間的規模:s.capacity()。從s[1]處插入:s.insert(1,"hua");s.insert(8,tag+' ')從末尾追加:s.append("dfd");
替換:s.replace(4,5,"dfdf");從s[4]開始替換5個字符。查找:i=s.find(tag,0)從s[0]開始查找,默認find(tag)從0開始查找。返回size_t,判斷查找到末尾:if(i==string::npos)
替換:replaceAll(s,"hua","XXX")將S的所有hua字符串替換為XXX
8、string::replace()一次只替換一次。算法replace(s.begin(),s.end(),'X','Y')將某個字符全部用另一個字符換掉。s=s1+s2.使用operator + 和operator +=
9、字符串查找:find,rfind,find_first_of找第一個與指定數值中任一個字符匹配,find_last_of,find_first_not_of,find_last_not_of
10、toupper,tolower(char),<cctype>
11、反向查找s.rfind.從后向前查找,不成功返回string::npos
12、從字符串中刪除字符:s.erase(0,string::npos),兩個默認值,起始位置,刪除的個數。s.erase()刪除所有。
13、<string>全局函數getline(in,s).使用s.c_str()可以將string類型轉換成char * 類型,
14、字符串比較string first,string second. first.compare(second)==0,>0.<0, first.compare(1,7,second,1,7),比較first從first[1]開始的7個字符和second[1]開始的7個字符。s[n]和s.at(n)一樣,但是s.at(n)可以拋出異常out_of_range()。
15.字符串交換:first.swap(second)
16、文件操作:string fname,刪除文件:remove(fname.c_str()),目錄存在:if(!exist(fname)).ifstream in(fname.c_str()),if(!in){exit(EXIT_FAILURE);}
第四章
1、#ifndef #define #endif 最主要的目的是防止頭文件被重復包含與編譯。
2、1-while(fgets(buf,size,fp));2-fputs(buf,stdout);3-while((c=fgetc(fp))!=EOF);4-fputc(c,fp);5-fread(buf,size,count,fp),每個count有size個字符,6-fwrite(buf,size,count,fp);7-while(!feof(fp));8-fflush(fp);9-fseek(fp,offset,fromwhere);formwhere:SEEK_SET:0,SEEK_END:2,SEEK_CUR:1;10-ftell(fp);文件指針相對文件首偏移字節數。
11-rewind(fp);將指針移至文件開頭。12-memcpy(newdata,data,sizeof()*count);
3、ostream &os;char fillc=os.fill('0');填充'0',返回先前的填充。os<<setfill(fillc)<<endl;恢復先前的填充。os<<setw(4)<iomanip>
4、按行輸入:成員函數get(),成員函數getline(),全局函數getline ()。1-2-有三個參數:指向緩沖區的指針、緩沖區大小、結束字符:默認'\n',while(in.get(buf,SZ) in.get(); 第一個get讀SZ-1個字符,遇到結束符。用get()跳過輸入流中的結束符。或者使用ignore(int N=1,char c=EOF),跳過的字符的數目,默認為1,遇到C字符時,函數退出,默認為EOF。 ? while(in.getline(buf,SZ)在遇到結束字符的時候,兩個都會在結果緩沖區末尾存儲一個0,區別:當遇到結束符,get停止執行,不從輸入流中提取結束符。再調用get,會遇到同一個結束符,函數將立即返回而不會提取輸入。而getline相反,將從輸入流中提取結束符,但仍然不會把它存儲到結果緩沖區中。get的重載版本:1-int get(),沒有參數,返回int類型的下一個字符.2-get(char &),從流中讀取一個字符放到這個參數中。3-把流對象存儲到基礎的緩沖區中。
5、處理流錯誤:ios::badbit,發生致命錯誤,流不能繼續使用。ios::eofbit,輸入結束,ctrl+D. ios::failbit,輸入非法數據,流可以繼續使用。ios::goodbit:正常。清空標志位:myStream.clear();設置某個標志位,重置另一個標志位:myStream.clear(ios::failbit|ios::eofbit);
6、打開剛創建的文件:ofstream out("file.out");ifstream in("file.out");對同一個文件打開讀寫要確定關閉文件。方法:一、使用大括號,是對象離開作用域調用析構函數,{out}in.二、使用out.close().
7、文件打開模式:ios::in打開輸入文件ios::out打開輸出文件ios::app打開一個僅用于追加的輸出文件ios::ate打開一個已存在的文件,并將文件指針指向文件末尾ios::trunc如果文件存在,截斷舊文件。ios::binary以二進制方式打開文件。回車/換行:0x0D/0x0A
8、流緩沖:streambuf對象將<<右側對象中的字符輸出到左側的對象中, ?ifstream in("i.cpp");cout<<in.rdbuf();輸出文件內容到標準輸出。rdbuf返回一個指針。
9、ios::beg流的開始位置,ios::cur流的當前位置,ios::end流的末端位置,p表示寫指針,g表示讀指針。ostream 調用tellp,seekp. ?istream 調用tellg,seekg. ? ofstream out("",ios::out|ios::binary);out.write(origData[i],STR_LEN). ifstream in("",ios::in|ios::binary);in.read(readData[i],TR_LEN);in.seekg(-STR_LEN,ios::end);//Seek -STR_KEN byte from the end of file
10、char readData[STR_NUM][STR_LEN]={{0}};
11、字符串輸入輸出流iostringstream直接對內存而不是對文件和標準輸出設備進行操作。它使用和cin、cout、相同的讀取和格式化函數來操縱內存中的數據。寫字符到字符串流,使用ostringstream,從這個流讀取字符,可以使用istringstream。頭文件<sstream>.epsilon()返回在<limits>中定義的常量,表示雙精度數字的機器誤差。 istringstream s("47 1.414 this is a test");int i;double f;s>>i>>f; ?ostringstream將字符串插入動態緩沖區,調用str()將內存中字符串轉換string。 cin >> ws; // Throw away white space
? string stuff;
? getline(cin, stuff); // Get rest of the line
? cout<<stuff;
? ostringstream os;
? os << "integer = " << i << endl;
? os << "float = " << f << endl;
? os << "string = " << stuff << endl;
? string result = os.str();
? cout << result << endl;
重載版本的os.str("huaguanglu");cout<<os.str()<<endl;
12、格式化標志:setf()打開標志,unsetf()關閉標志。標志:ios::skipws 跳過空格 ios::showbase打印整型時指出數字的基數(dec/oct/hex)。ios::showpoint顯示浮點值的小數點。ios::uppercase顯示十六進制時,使用大寫。ios::showpos:顯示正數前面的'+'。ios::unitbuf:單元緩沖區。立即刷新。cout.setf(ios::showpos)顯示正號cout.unsetf(ios::showpos)關閉顯示。
13、格式化域:ios::basefield (ios::dec使整型數值的基數為10,ios::hex使整型數值的基數為16,ios::oct使整型數值的基數為8).ios::floatfield(ios::scientific以科學計數的形式顯示浮點數.ios::fixed以固定格式顯示浮點數。)ios::adjustfield(ios::left使數值左對齊,填充字符右邊空位。ios::right,ios::internal填充字符放在正負號和數值之間正負號左對齊,數值右對齊),cout.setf(ios::fixed,ios:floatfield);
14、寬度、填充、精度:int ios::width()返回當前寬度,默認為0,in ios::width(int n)設定寬度,返回先前。,int ios::fill()返回當前填充字符,默認為空格,int ios::fill(int n)設定填充字符,返回先前,int ios::precision()默認精度小數點后面六位,int ios::precision(int n)設定精度,返回先前。寬度不會截斷字符。寬度只規定最小長度。
cout.setf(ios::boolalpha)返回true、false,代替0,1
15、操縱算子:刷新流:cout<<endl;cout<<flush;在頭文件<iostream>中包含操縱算子:cin>>ws吃掉空格,cout<<hex<<"0x"<<i<<endl;
endl,flush,hex,oct,dec,
showbase,noshowbase,showpos,noshowpos,showpoint,noshowpoint,
uppercase,nouppercase,skipws,noskipws,
left,right,internal,
scientific,fixed,
setiosflags()相當于ios::setf(),resetiosflags,相當于ios::unsetf(),
setbase(base n),n取值8,10,16,輸出的基數。setfill(char n)相當于ios::fill(n),
setprecision(int n),setw(int n),?
舉例:
istringstream is("one 2.34 five");
? string temp;
? is >> setw(3) >> temp;temp="on".is >> setw(2) >> temp;temp="e".
16、const ulong ULMAX=numeric_limits<ulong>::max();<limits>
17、time_t timer;srand(time(&timer));
第五章
1、模板參數:無類型模板參數template<class T,size_t N> class Stack{
T data[N];
size_t count;
public:
void push(const T& t);
};
實例化模板時,為參數N提供一個編譯時常數Stack<int ,100> myStack;
2、默認模板參數:template<class T, size_t N=100> class Stack{};實例化:Stack<int> stack;
template<class T, class Allocator=allocator<T> > class vector;vector有兩個參數,一個參數表示它持有的包含對象的類型。另一個參數表示vector所使用的分配器。
函數模板不可以使用默認的模板參數。卻能使用模板參數作為普通函數的默認參數。
template<class T> T sum(T* b, T* e, T init=T()){}main{int a[]={1,2,3};cout<<sum(a,a+sizeof a/ sizeof *a)<<endl;}參數默認是T(),int()執行零初始化。int a=int();
3、模板類型的模板參數:
template<class T> class Array{
public:
void push_back(const T& t){}
T* begin(){return data;}
T* end(){return data+count;}
};
template<clss T, template<class> class Seq> class Container{
Seq<T> seq;
public:
void append(const T& t){seq.push_back(t);}
T* begin(){return seq.begin();}
T* end(){rturn seq.end();}
}
main()
{
Container<int, Array> container;
}
此時的默認參數需要重新聲明:
template<class T, size_t T=10>
template<class T,template<class,size_t = 10> class Seq>
typename Seq<T>::iterator begin(){return seq.begin();}
typename T::id i;
typename作用:
1-typename不是創建一個新類型,而是聲明id是一個嵌套類型,然后創建這個類型的對象 i。
class Y{
class id {};
};
創建一個新類型:typedef typename Seq<It>::iterator It;
2-typename代替class
4、<typeinfo> cout<<typeid<T*this>.name(),返回type_info對象。成員模板函數不能為virtual虛類型。
5、min<>(1,4),表示強制使用模板。<cctype>toupper/tolower有一個參數,<iostream>toupper/tolower有兩個參數。避免沖突:
1-可以在不包含<iostream>中使用,2-可以使用封裝的函數模板:template<class charT> charT strTolower(charT c){return tolower(c);}3-可以使用類型轉換:
transform(s.begin(),s.end(),s.begin(),static_cast<int (*)(int)>tolower);
6、模板特化:template<>告訴編譯器接下來是模板特化。template<> const char* const&min<const char*>(const char* const &a,const char* const &b){retrn (strcmp(a,b)<0)?a:b;}
7、模板不是代碼,是產生代碼的指令。防止模板代碼膨脹:一個模板化的Stack容器,用int,int *,char*特化,會生成3個版本的Stack代碼,可以用void* 進行完全特化,然后從void*實現中派生出所有的其他的指針類型。
8、名稱查找問題:編譯器第一階段對模板解析,第二階段模板實例化。限定名:MyClss::f()/x.f()/p->f() ?關聯參數查找。若名稱是關聯的,則它的查找在實例化進行,非限定的關聯名稱除外,它是一個普通的名稱查找,它的查找在定義時進行,比較早。
9、在類中聲明一個友元函數,就允許一個類的非成員函數訪問這個類的非公有成員。友元函數模板必須提前聲明。
template<class T> class Friendly;
template<class T> void f(const Friendly<T>&);
template<class T> class Friendly{
public:
friend void f<>(const Friendly<T>&);//f<>說明f是一個模板
friend void f(const Friendly<T>&);//f說明f是一個普通函數
};
10、模板元編程:編譯時編程,在程序開始運行前就已經完成了計算。
template<int n> struct Factorial {
? enum { val = Factorial<n-1>::val * n };
};
template<> struct Factorial<0> {
? enum { val = 1 };
};
int main() {
? cout << Factorial<12>::val << endl; // 479001600
} ///:~
11、模板定義的源代碼與它的聲明相分離,使用export關鍵字。
export
template<int n> struct Factorial {
? enum { val = Factorial<n-1>::val * n };
};
第六章
<algorithm>
1、copy: int a[SZ];int b[SZ];copy(a,a+SZ,b); ? ? equal: equal(a,a+SZ,b);相等返回true.
2、vector<int> v1(a,a+SZ),v2(SZ),v3;//構造函數初始化,v1用a初始化,v2用SZ作分配空間大小。v3沒有確定大小。equal/copy(v1.begin(),v1.end(),v2.begin()),
copy(v1.begin(),v1.end(),back_inserter(v3)), back_inserter在頭文件<iterator>中定義。 ?equal(v1.begin(),v1.end(),v3.begin())
3、取<=15的數:bool gt15(int x){return 15<x;} ?remove_copy_if(a,a+SZ,b,gt15);返回忽略gt15為真的值(b.end())。bool contains_e(const string&s){return s.find('e') != string::npos;} ?replace_copy_if(a,a+SZ,b,contains_e,string("kiss")); ?返回b.end() ? ? ? ? replace_if(a,a+SZ,contains_e,string("kiss"))
4、流迭代器:<iterator> ostream_iterator<int>(cout,'\n').?
?remove_copy_if(a,a+SZ,ostream_iterator<int>(cout,'\n'),gt15);?
ofstream out();remove_copy_if(a,a+SZ,ostream_iterator<int>(out,'\n'),gt15);?
ifstream in(); remove_copy_if(istream_iterator<int>(in),istream_iterator<int>(),ostream_iterator<int>(cout,'\n'),gt15); ?
istream_iterator<int>()默認構造函數表示文件的結束
5、size_t n=count_if(a,a+SZ,gt15);產生>15的整數元素的個數。int *p=find(a,a+SZ,20)搜索一個序列,直到遇到等于第三個參數的元素。返回第一次找到的指針位置。
6、函數對象:它實現起來像函數同時保存狀態,使用時卻不用考慮函數參數的個數。這種抽象叫函數對象。
<functional> 提供兩種類型:unary_function和binary_function.
?remove_copy_if(a,a+SZ,b,bind2nd(greater<int>,15))
int a[]={12,20,12};count_if(a,a+SZ,not1(bind1st(equal_to<int>(),20)))
產生標準函數對象的模板表達式:類型:unary_function和binary_function.
plus<int>() /minus/multiplies/divides/modulus/negate/equal_to/not_equal_to/greater/less/greater_equal/less_equal/logical_and/logical_or/logical_not
tansform(v.begin(),v.end(),v.begin(),bind2nd(multiplies<int>(),10))用10乘以v中每個元素
7、sort(a,a+SZ),
8、函數指針適配器ptr_fun()把一個指向函數的指針轉化成一個函數對象。
int d[] = { 123, 94, 10, 314, 315 };
const int DSZ = sizeof d / sizeof *d;
bool isEven(int x) { return x % 2 == 0; }
int main() {
? vector<bool> vb;
? transform(d, d + DSZ, back_inserter(vb),
? ? not1(ptr_fun(isEven)));
? copy(vb.begin(), vb.end(),
? ? ostream_iterator<bool>(cout, " "));
? cout << endl;
? // Output: 1 0 0 0 1
} ///:~
9、for_each(v.begin(),v.end(),mem_fun(&shape::draw))算法將序列中每一個元素一次傳遞給第三個參數指示的函數對象。mem_fun()使用指向成員的一個指針來作為它的參數。mem_fun_ref()為一個對象直接調用成員函數
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
#include "../purge.h"
using namespace std;
class Shape {
public:
? virtual void draw() = 0;
? virtual ~Shape() {}
};
class Circle : public Shape {
public:
? virtual void draw() { cout << "Circle::Draw()" << endl; }
? ~Circle() { cout << "Circle::~Circle()" << endl; }
};
class Square : public Shape {
public:
? virtual void draw() { cout << "Square::Draw()" << endl; }
? ~Square() { cout << "Square::~Square()" << endl; }
};
int main() {
? vector<Shape*> vs;
? vs.push_back(new Circle);
? vs.push_back(new Square);
? for_each(vs.begin(), vs.end(), mem_fun(&Shape::draw));
? purge(vs);
} ///:~
class Angle {
? int degrees;
public:
? Angle(int deg) : degrees(deg) {}
? int mul(int times) { return degrees *= times; }
};
transform()要求它調用的函數返回一個值。不能使用transform()調用返回值為void的成員函數,也不能調用只有一個參數的for_each函數
int main() {
? vector<Angle> va;
? for(int i = 0; i < 50; i += 10)
? ? va.push_back(Angle(i));
? int x[] = { 1, 2, 3, 4, 5 };
? transform(va.begin(), va.end(), x,
? ? ostream_iterator<int>(cout, " "),
? ? mem_fun_ref(&Angle::mul));
? cout << endl;
? // Output: 0 20 60 120 200
} ///:~
vector<string>v;
vector<string>::ietrator it;
it=find_if(v.begin(),v.end(),mem_fun_ref(&string::empty));
10、string-->char *-->atof
transform(v.begin(),v.end(),back_inserter(b),mem_fun_ref(&string::c_str));
transform(b.begin(),b.end(),back_inserter(c),std::atof);
11、填充和生成: vector<string> v1(5); ?vector<string> v2;
fill(v1.begin(),v1.end(),"ddd"); ?fill_n(back_inserter(v2),7,"bye"); ? ? ? ?fill_n (first,n,value)對由first開始的n個元素賦值value。
generate(v1.begin(),v1.end,gen()); 對區間每個元素調用gen(). ? generate_n(back_inserter(v2),7,gen())對gen()調用7次,將返回值賦給由first開始的7個元素。
12、計數:count(v.begin(),v.end(), value); ?count_if(v.begin(),v.end(),pred)//pred返回true的個數
13、操作序列:
copy(v.begin(),v.end(),dest.begin())
copy_backward(v.begin(),v.end(),dest.begin())//以相反的順序復制元素
reverse(v.begin(),v.end())//倒置原序列范圍的元素
reverse_copy(v.begin(),v.end(),dest.begin())
swap_ranges(v.begin().v.end(),v2.begin())//交換相等大小兩個范圍的內容
rotate(first,middle,last)//[first,middle)內容移至末尾,[middle,last)元素移至開始
rotate_copy(first,middle,last,dest)
next_permutation(first,last)//排列成后繼的排列
next_permutation(first,last,pred)//重載
pre_permutation(first,last)//排列成前驅的排列
pre_permutation(first,last,pred)//重載
random_shuffle(first,last)//隨機重排范圍內的元素
random_shuffle(first,last,rand)//重載,使用用戶提供的隨機函數重排
partition(first,last,pred)//劃分
stable_partition(first,last,pred)
14、查找和替換:
find(first,last,value)
find_if(first,last,pred)//pred==true 返回
adjacent_find(first,last)//兩個鄰近相等
adjacent_find(first,last,pred)//查找相鄰兩個符合pred的元素
find_first_of(first1,last1,first2,last2)
find_first_of(first1,last1,first2,last2,pred)
search(first1,last1,first2,last2)//第二序列是否出現在第一序列范圍之內,順序完全一致,返回首先出現的位置
search(first1,last1,first2,last2,pred)//比較的每對元素是否使pred為true.
find_end(first1,last1,first2,last2)//第二序列是否是第一序列的子集,返回最后出現的位置
find_end(first1,last1,first2,last2,pred)//比較的每對元素是否使pred為true.返回最后出現的位置
search_n(first,last,value)//找count個連續的值,與value相等。
search_n(first,last,value,pred)//與value相等的值傳遞給pred,返回true,否則返回last.
min_element(first,last)//最小值首次出現的位置
min_element(first,last,pred)//返回r,對(first,r)范圍中的元素使pred(*e,*r)都為假。
replace(first,last,old_value,new_value);
replace_if(first,last,pred,new_value);
replace_copy(first,last,result,old_value,new_value);
replace_copy_if(first,last,result,pred,new_value);
15、比較范圍:
equal(first1,last1,first2)
equal(first1,last1,first2,pred)
lexicographical_compare(first1,last1,first2,last2)//范圍1序列元素小于范圍2序列元素
lexicographical_compare(first1,last1,first2,last2,pred)
pair<InputIterator1,InputIterator2> ? pair在頭文件<utility>中定義
mismatch(first1,last1,first2)//比較兩個序列從哪兒開始不同,將兩個位置裝入pair返回,用first,second訪問元素。
mismatch(first1,last1,first2,pred)//比較兩個序列從哪兒開始不同,將兩個位置裝入pair返回,用first,second訪問元素。
16、刪除元素:
remove(first,last,value)//返回new_last
remove_if(first,last,pred)//返回new_last
remove_copy(first,last,result,value)//返回new_last
remove_if(first,last,result,pred)//返回new_last
真正刪除:c.erase(remove(c.begin(),c.end(),value),c.end);
unique(first,last)//刪除相鄰的相等值的副本,使用unique前,需要先sort().
unique(first,last,pred)
unique_copy(first,last,result)
unique_copy(first,last,result,pred)
17、排序和運算
排序:
sort(first,last)//升序
sort(first,last,pred)
stablesort(first,last)//升序,保持相等元素的原始順序
stablesort(first,last,pred)
partial_sort(first,middle,last)//對一定數量元素排序,放入[first,middle)中,范圍[middle,last)元素不保證有序
partial_sort(first,middle,last,pred)
partial_sort_copy(first1,last1,first2,last2)
partial_sort_copy(first1,last1,first2,last2,pred)
nth_element(first,nth,last)//也是對一部分處理,[first,nth)元素滿足 operator <,而[nth,last)都不滿足。
nth_element(first,nth,last,pred)
運算:
在已排序的序列中查找
binary_search(first,last,value)
binary_search(first,last,value,pred)
lower_bound(first,last,value)//第一次出現的位置,若沒有,返回應該出現的位置
lower_bound(first,last,value,pred)
upper_bound(first,last,value)//超過value最后出現的位一個置,若沒有,返回應該出現的位置
upper_bound(first,last,value,pred)
pair<ForwardIterator1,ForwardIterator2> ? pair在頭文件<utility>中定義
equal_range(first,last,value)//結合了lower_bound和upper_bound,返回一個指出value在已排序的范圍[first,last)中首次出現和超越最后出現的pair,如果沒有找到,這兩個迭代器都指 //出value在該序列中應該出現的位置。
equal_range(first,last,value,pred)
合并已排序的序列
merge(first1,last1,first2,last2,result)
merge(first1,last1,first2,last2,result,pred)
inplace_merge(first,middle,last)//[first,middle)有序,[middle,last)有序,兩個合起來
inplace_merge(first,middle,last,pred)
在已排序的序列上進行集合運算
includes(first1,last1,first2,last2)//序列2是1的子集,返回true
includes(first1,last1,first2,last2,pred)//序列2是1的子集,返回true
set_union(first1,last1,first2,last2,result)//求并集,返回result.end().
set_union(first1,last1,first2,last2,result,pred)
set_intersection(first1,last1,first2,last2,result)//求交集,返回result.end().
set_intersection(first1,last1,first2,last2,result,pred)
set_difference(first1,last1,first2,last2,result)//求集合的差,返回result.end().
set_difference(first1,last1,first2,last2,result,pred)
set_symmetric_difference(first1,last1,first2,last2,result)//求在集合1不在集合2,在集合2不在集合1,返回result.end().
set_symmetric_difference(first1,last1,first2,last2,result,pred)
18、堆運算
make_heap(first,last)//建堆
make_heap(first,last,pred)
push_heap(first,last)//向范圍[first,last-1)堆中增加元素*(last-1)
push_heap(first,last,pred)
pop_heap(first,last)//將最大的元素 *first 放入位置*(last-1)并且重新組織剩余的范圍。
pop_heap(first,last,pred)
sort_heap(first,last)//轉化成普通的排列順序,是不穩定的排序
sort_heap(first,last,pred)
19、對某一范圍內的所有元素進行運算
UnaryFuntion for_each(first,last,UnaryFuntion f);//對范圍中每個元素應用f,最終返回值是f
OutIterator transform(first,last,OutIterator result,UnaryFunction f)//f(*first),返回result.end()
OutIterator transform(first,last,first2,OutIterator result,BinaryFunction f)//f(*first,*first2),返回result.end()
20、數值算法
<numeric>
T accumulate(first,last,T result)//i指向[first,last)中的每一個元素,result = result + *i
T accumulate(first,last,T result,BinaryFunction f)//i指向[first,last)中的每一個元素,對每個*i應用f(result,*i)
T inner_product(first1,last1,first2,T init)//init是內積的初始值,可能是0或其他值。廣義內積。{1,2,3} {2,3,4}=1*2+2*3+3*4=20
T inner_product(first1,last1,first2,T init,BinaryFunction op1,BinaryFunction op2)//init是內積的初始值。op1 代替加法,op2代替乘法{1,2,3} {2,3,4}=1 op2 2 op1 2op2..
partial_sum(first,last,result)//廣義部分和。{1,2,3} 即:{1,1+2,1+2+3}={1,3,6}
partial_sum(first,last,result,BinaryFunction op)//op代替 +?
adjacent_difference(first,last,result)//廣義部分差。{1,2,3} 即:{1,2-1,3-2}={1,1,1}
adjacent_difference(first,last,result,BinaryFunction op)//op代替 -?
21、通用
swap(a,b)
iter_swap(it1,it2)
min(a,b)
max(a,b)
第七章
1、vector 高效訪問。list 高效插入。
set<int> intset; int i=10;
intset.insert(i);//自動排序,并且元素唯一。
2、容器分類:
序列容器: vector、list、deque(雙端隊列) 都有push_back(),list和deque還有一個push_front(),vector和deque可以使用operator[],但是list不支持
容器適配器:queue、stack、priority_queue
關聯式容器:set、map、multiset、multimap
3、vector<string>::iterator it; reverse_iterator it=v.rbegin();rend();
4、back_inserter(v);front_inserter(v);it++,it++,inserter(v,it)
istream_iterator<char>和ostream_iterator<char>會吃掉空白字符。
使用:istreambuf_iterator<char>和ostreambuf_iterator<char>
5、序列容器:vector、list、deque
typedef Container ?C ; C s;
s.empty()//判空
s.size()//尺寸
s.max_size()//最大可能尺寸
s.front()//起始元素
s.back()//終止元素
C s(10,1)//10個1
int a[SZ];
c.assign(a,a+SZ);
c.assign(10,2)//10個2
c.push_back(47);
c.pop_back()//刪除尾部
typedef C::iterator it=c.begin();it++;
c.insert(it,47);
c.insert(it,3,86);//插入3個86
c.insert(it,c2.bein(),c2.end())
c.erase(it)
c.erase(it,it2)//清除序列中間的元素
c.swap(c2)//兩個容器交換所有
c.clear()//刪除容器中全部內容
6、向量:vector 索引和迭代操作速度快,除了在最后一個元素之后插入新元素外,向vector中插入一個對象是不可接受的操作。
? ? 副作用:當一個vector與存儲空間用完以后,為維護其連續的對象數組,它必須在另一個地方重新分配大塊新的存儲空間,并把以前已有的對象拷貝到新的 存儲空間中去。
? ? 已分配存儲區溢出的代價:1-分配一塊新的更大的存儲區。2-將舊存儲區中的對象拷貝到新開辟的存儲區中去,使用拷貝構造函數。3-銷毀舊存儲區中的所有對象。4-釋放舊存儲區的內存。
? ? 如果vector經常被填滿,系統將會為這些拷貝構造函數和析構函數操作的完成付出高昂的代價。
? ? 為了支持排序和查找等操作,需要有運算符operator< 、 operator==。int size=atoi("1000");<stdlib>
? ? 使用vector最有效的條件是:1-在開始時使用reserve()分配了正確數量的存儲區。因此vector不再重新分配存儲區。2-僅僅在序列的后端添加或刪除元素。利用迭代器向vector中間插入或者刪除元素是可能的。
7、雙端隊列:deque,在序列兩端對元素添加和刪除。允許快速隨機訪問。支持operator[]
? ? deque允許在序列兩端快速地插入和刪除元素,并且在其擴展存儲區的時候不需要拷貝和銷毀對象的操作。
? ? 1-deque沒有像vector的那種把所有東西保存在一塊連續的內存中的約束。利用多個連續存儲塊。2-不需要在分配新的存儲區是復制并銷毀所包含的對象。3-deque有push_front(),pop_front().
<ctime>: clock_t ticks=clock(); ticks=clock()-ticks;cout<<ticks<<endl;
添加行號:
for(size_t i=0;i<v.size();i++)
{
ostringstream ss;
ss<<i;
v[i]=ss.str()+":"+v[i];
}
vector、deque都提供隨機訪問,使用索引操作符operator[],使用at(),越界則拋出異常。使用at()代價更高一些。
8、鏈表: list:雙向鏈表數據結構。在任何地方快速插入或刪除元素。
list<string> l,l2;
l.reverse()//倒置
l.sort()//排序
swap(*it1,*it2)
reverse(l.begin(),l.end());//起作用
通用的sort()算法僅僅適用于數組、vector和deque
list<string>::iterator it1,it2;
it1=l.begin();
l.splice(it1,l2)//鏈表l在it1處開始結合鏈表l2,注意:結合之后l2是空的。
l.splice(it1,l2,it2)//鏈表l在it1處開始結合鏈表l2[it2]
l.splice(it1,l2,it2,it3)//鏈表l在it1處開始結合鏈表l2從it2開始終止在it3.
l2.remove(l2.back());//刪除具有特定值的所有元素
l2.remove(l2.front());
?l.merge(l2);//合并成倆個表,
?l.sort();
?l.unique();//從list中刪除所有相鄰的重復的對象,唯一的條件就是首先對list進行排序。
l.swap(l2)
9、集合:set僅接受每個元素的一個副本,并對元素排序。set是用一個平衡樹數據結構來存儲其元素以提供快速的查找。
set<string> s;
inserter(s,s.begin());
char c; bool isalpha(c);
string word;
s.insert(word)
insert_iterator<string> ii(word,word.begin())//從何處開始插入
while(p!=end)
{
*ii++=*p++:
}
s.insert(word);
ifstream in(fname);
istreambuf_iterator<char>p(in),end//這個迭代器從流中逐字符地 提取信息。
while (p!=end)
10、堆棧。stack,與queue、priority_queue一起被歸類為適配器。建立在另一個序列容器的基礎之上。它們將通過調整某一個序列容器以存儲自己的數據。
stack<string> Stack1;//默認使用deque
stack<string, vector<string> > Stack2
stack<string, list<string> > Stack3
string line;
Stack1.push(line+"\n");
while(!Stack1.empty()){
Stack1.top();
Stack1.pop();
}
可以使用一個vector及其back(),push_back(),pop_back()獲得等價的堆棧功能,還擁有vector 的附加功能。
11、隊列 queue,容器默認的模板參數是deque.
queue<string> que;
if(!que.empty())
{
cout<<que.front();
que.pop();
}
12、優先隊列 priority_queue #include <queue>
當向一個優先隊列中用push壓入一個對象時,那個對象根據一個比較函數或函數對象在隊列中排序。priority_queue確定在用top查看頂部元素時,該元素將是具有最高優先級的一個元素。處理完該元素,用pop刪除。
priority_queue<int> pqi;//
pqi.push(i);
while(!pqi.empty())
{
cout<<pqi.top();
pqi.pop();
}
改變優先級
priority_queue<int,vector<int>,greater<int> > pqi;//
0 0 0 0 1 1 2 2 3 3 3 3 3
1 11 11 11 11 12 12 12 12
17 17 17 17 17 18 18 18 1
?23 23 23 24 24 24 24 24
priority_queue<int,vector<int>,less<int> > pqi;//===priority_queue<int > pqi;//
24 24 23 23 23 23 2
?18 18 17 17 17 17
1 11 11 10 10 10 10
2 2 1 1 1 1 1 1 1 0
堆就是一個優先隊列:make_heap(),push_heap(),pop_heap(),priority_queue只是對它的封裝。使用sort_heap之后,再使用make_heap,轉換成堆。
13、持有二進制位:二進制位集合bitset和邏輯向量vector<bool>不同點:1-bitset持有固定二進制位,vector<bool>動態擴展。2-bitset沒有迭代器,vector<bool>是vector的特化。3-bitset與STL不相似,vector<bool>類似于類STL 容器。
bitset參數表示二進制位的個數:bitset<10>與bitset<20>是兩種不同的類型,不能將它們兩個之間進行比較、賦值等操作。從bitset到一個數的轉換用to_ulong()
bitset:
#include <bitset>
typedef bitset<32> BS;
BS a(rand());
unsigned long ul=a.to_ulong();
string bits("01111110");
BS a(bits);
BS b("11111110");
cout<<BS(bits)<<endl;//顯示一個完整string,不夠在前補0至32位,多了則刪除后面的。
cout<<BS(bits,2)從第二個字符開始,不夠在前補0至32位,多了則刪除后面的。
cout<<BS(bits,2,11)從第二個字符開始連續11個字符,不夠在前補0至32位,多了則刪除后面的。
cout<<a<<endl;//輸出二進制a
cout<<(a&b)<<endl;//00000000000000000000000001111110
cout<<(BS(a)&=b)
cout<<(a|b)<<endl;
cout<<(BS(a)|=b)
cout<<(a^b)<<endl;
cout<<(BS(a)^=b)
cout<<(BS(a)<<=16)
cout<<(BS(a)>>=16)
cout<<BS(a).set()//11111111111111111111111111111111
a.test(0)==0, a.test(1)==1//從右往左下標:76543210
cout<<BS(a).set(i)
cout<<BS(a).reset();//00000000000000000000000000000000
cout<BS(a).flip()//按位取反
cout<BS(a).flip(0)//按位取反第0位。
cout<<a.count()//1的個數
cout<<a.any()//有1存在?
cout<<a.none()//沒有1存在?
cout<<a.size()//BS 的二進制位數
vector<bool>:是vector模板的特化,
沒有set,reset,只有在vector基礎上加了flip
?ostringstream os;
?copy(vb.begin(), vb.end(),ostream_iterator<bool>(os, ""));
?bitset<10> bs(os.str());
?cout << "Bitset:" << endl << bs << endl;
14、關聯式容器 set/map/multiset/multimap 它們將關鍵字與值關聯起來。set可以看成是沒有值的map,它只有關鍵字。
關聯式容器最重要的操作是將對象放進容器。在set的情況下要查看該對象是否已在集合中,在map情況下先查看關鍵字是否已在map中,如果存在,就為關鍵字設置關聯值。
set<Noisy> s(a,a+SZ);
Noisy n;
s.insert(n);
cout<<s.count(n);//關鍵字出現的次數:0/1
if(s.find(n)!=s.end())
map<int , Noisy> nm;
for(i=0;i<10;i++){
nm[i];//自動創建對象,如果使用operator[]查找一個值而它又不存在的話,這個map就會創建一個新的關鍵字-值對。即:如果實際上想要查詢某個對象并不想創建一個新條目,就必須使用個成員函數count()或者find().
}
nm.insert(make_pair(47.n))
nm.count(10);//
map<int ,Notify>::iterator it=nm.find(6);//關鍵字出現的次數:0/1
cout<<(*it).first<<(*it).second;
typedef pair<const Key, T> value_type
set<int>s;
fill_n(inserter(s,s.begin()),10,7);
map<int,int>m
fill_n(inserter(m,m.begin()),10,make_pair(90,120))
copy(m.begin(),m.end(),ostream_iterator<pair<int,int> >(cout,"\n"));
multiset:可以插入每個值的多個對象所有相鄰的元素必須毗鄰存放。
清除容器的指針:
Container shapes;
shapes.push_back(new Circle);
...
for(it=shapes.begin(),it!=shapes.end();it++)
{
delete *it;
*it=0;
}
#ifndef PURGE_H
#define PURGE_H
#include <algorithm>
template<class Seq> void purge(Seq& c) {
? typename Seq::iterator i;
? for(i = c.begin(); i != c.end(); ++i) {
? ? delete *i;
? ? *i = 0;
? }
}
// Iterator version:
template<class InpIt> void purge(InpIt begin, InpIt end) {
? while(begin != end) {
? ? delete *begin;
? ? *begin = 0;
? ? ++begin;
? }
}
#endif // PURGE_H ///:~
第八章
1、通過指針或引用來決定對象運行時類型的一種方法是使用運行時類型轉換。此方法適合把基類指針類型轉換為派生類型,也稱向下類型轉換。
class Bond : public Security {
? typedef Security Super;
protected:
? enum { OFFSET = 2, TYPEID = BASEID + OFFSET };
public:
? bool isA(int id) {
? ? return id == TYPEID || Super::isA(id);
? }
? static Bond* dynacast(Security* s) {
? ? return (s->isA(TYPEID)) ? static_cast<Bond*>(s) : 0;
? }
};
dynamic_cast提供類型轉換檢查。
class Security {
public:
? virtual ~Security() {}
};
class Stock : public Security {};
class Bond : public Security {};
class Investment : public Security {
public:
? void special() {
? ? std::cout << "special Investment function" <<std::endl;
? }
};
class Metal : public Investment {};
使用指針:
vector<Security*> portfolio;
? portfolio.push_back(new Metal);
? portfolio.push_back(new Investment);
? portfolio.push_back(new Bond);
? portfolio.push_back(new Stock);
? for(vector<Security*>::iterator it =
? ? ? ?portfolio.begin();
? ? ? ?it != portfolio.end(); ++it) {
? ? Investment* cm = dynamic_cast<Investment*>(*it);
? ? if(cm)
? ? ? cm->special();
? ? else
? ? ? cout << "not a Investment" << endl;
? }
? cout << "cast from intermediate pointer:" << endl;
? Security* sp = new Metal;
? Investment* cp = dynamic_cast<Investment*>(sp);
? if(cp) cout << " ?it's an Investment" << endl;
? Metal* mp = dynamic_cast<Metal*>(sp);
? if(mp) cout << " ?it's a Metal too!" << endl;
dynamic_cast<目標類型>(操作數),dynamic_cast要求使用的目標對象的類型是多態的,這就要求該類必須至少有一個虛函數。轉換失敗拋出bad_cast ? <typeinfo>異常。
使用引用:
Metal m;
? Security& s = m;
? try {
? ? Investment& c = dynamic_cast<Investment&>(s);
? ? cout << "It's an Investment" << endl;
? } catch(bad_cast&) {
? ? cout << "s is not an Investment type" << endl;
? }
? try {
? ? Bond& b = dynamic_cast<Bond&>(s);
? ? cout << "It's a Bond" << endl;
? } catch(bad_cast&) {
? ? cout << "It's not a Bond type" << endl;
? }
2、type_info ::name(); typeid 返回type_info 類的對象,typeid獲得動態類型的名稱。const PolyBase *ppdtypeid(*ppb)
3、類型轉換到中間層次類型
4、typeid不能與void型指針一起工作。void *v=new Security;//!cout<<typeid(*v).name();//!Security *s=dynamic_cast<Security*>(v);
5、帶模板的RTTI:
#include <iostream>
#include <typeinfo>
using namespace std;
template<int id> class Announce {
public:
? Announce() {
? ? cout << typeid(*this).name() << " constructor" << endl;
? }
? ~Announce() {
? ? cout << typeid(*this).name() << " destructor" << endl;
? }
};
class X : public Announce<0> {
? Announce<1> m1;
? Announce<2> m2;
public:
? X() { cout << "X::X()" << endl; }
? ~X() { cout << "X::~X()" << endl; }
};
int main() { X x; } ///:~
//output
class Announce<0> constructor
class Announce<1> constructor
class Announce<2> constructor
X::X()
X::~X()
class Announce<2> destructor
class Announce<1> destructor
class Announce<0> destructor
第九章
1、接口繼承:僅僅在一個派生類接口中加入了成員函數的聲明。除了析構函數以外,這些聲明都是純虛函數。
class Printable {
public:
? virtual ~Printable() {}
? virtual void print(ostream&) const = 0;
};
class Intable {
public:
? virtual ~Intable() {}
? virtual int toInt() const = 0;
};
class Stringable {
public:
? virtual ~Stringable() {}
? virtual string toString() const = 0;
};
class Able : public Printable, public Intable,
public Stringable {
int myData;
public:
Able(int x) { myData = x; }
void print(ostream& os) const { os << myData; }
int toInt() const { return myData; }
string toString() const {
ostringstream os;
os << myData;
return os.str();
}
};
2、實現繼承:在派生類中實現所有的 細節。
protected類型需要一個友元或派生類來使用它?;惖奈鰳嫼瘮凳翘摵瘮???梢员WC派生類的對象正確的銷毀。
3、重復子對象
當從某個基類繼承時,可以在派生類中得到那個基類的所有數據成員的副本。
class A { int x; };
class B { int y; };
class C : public A, public B { int z; };
int main() {
? cout << "sizeof(A) == " << sizeof(A) << endl;
? cout << "sizeof(B) == " << sizeof(B) << endl;
? cout << "sizeof(C) == " << sizeof(C) << endl;
? C c;
? cout << "&c == " << &c << endl;
? A* ap = &c;
? B* bp = &c;
? cout << "ap == " << static_cast<void*>(ap) << endl;
? cout << "bp == " << static_cast<void*>(bp) << endl;
? C* cp = static_cast<C*>(bp);
? cout << "cp == " << static_cast<void*>(cp) << endl;
? cout << "bp == cp? " << boolalpha << (bp == cp) << endl;
? cp = 0;
? bp = cp;
? cout << bp << endl;
}
/* Output:
sizeof(A) == 4
sizeof(B) == 4
sizeof(C) == 12
&c == 1245052
ap == 1245052
bp == 1245056
cp == 1245052
bp == cp? true
0
*/ ///:~
如果有多個基類,若果這些基類依次有一個共同的基類,那么將得到頂層基類的兩個副本,
如果有多個基類,派生類向上類型轉換會出現二義性。
class Top {
? int x;
public:
? Top(int n) { x = n; }
};
class Left : public Top {
? int y;
public:
? Left(int m, int n) : Top(m) { y = n; }
};
class Right : public Top {
? int z;
public:
? Right(int m, int n) : Top(m) { z = n; }
};
class Bottom : public Left, public Right {
? int w;
public:
? Bottom(int i, int j, int k, int m)
? : Left(i, k), Right(j, k) { w = m; }
};
int main() {
? Bottom b(1, 2, 3, 4);
? cout << sizeof b << endl; // 20
} ///:~
4、虛基類:消除向上類型轉換時二義性的問題。
class Top {
protected:
? int x;
public:
? Top(int n) { x = n; }
? virtual ~Top() {}
? friend ostream&
? operator<<(ostream& os, const Top& t) {
? ? return os << t.x;
? }
};
class Left : virtual public Top {
protected:
? int y;
public:
? Left(int m, int n) : Top(m) { y = n; }
};
class Right : virtual public Top {
protected:
? int z;
public:
? Right(int m, int n) : Top(m) { z = n; }
};
class Bottom : public Left, public Right {
? int w;
public:
Bottom(int i, int j, int k, int m)
: Top(i), Left(0, j), Right(0, k) { w = m; }
friend ostream&
operator<<(ostream& os, const Bottom& b) {
return os << b.x << ',' << b.y << ',' << b.z
<< ',' << b.w;
}
};
int main() {
Bottom b(1, 2, 3, 4);
cout << sizeof b << endl;
cout << b << endl;
cout << static_cast<void*>(&b) << endl;
Top* p = static_cast<Top*>(&b);
cout << *p << endl;
cout << static_cast<void*>(p) << endl;
cout << dynamic_cast<void*>(p) << endl;
} ///:~
//output
28
1,2,3,4
0012FF58
1
0012FF6C
dynamic_cast到void*的結果總是確定指向完整對象的地址。
5、含有虛基類的子對象的初始化順序
1-所有虛基類子對象,按照他們在了定義中出現的位置,從上到下,從左到右初始化。
2-然后非虛基類按通常順序初始化。
3-所有的成員對象按聲明的順序初始化。
4-完整的對象的構造函數執行。
class M {
public:
? M(const string& s) { cout << "M " << s << endl; }
};
class A {
? M m;
public:
? A(const string& s) : m("in A") {
? ? cout << "A " << s << endl;
? }
? virtual ~A() {}
};
class B {
? M m;
public:
? B(const string& s) : m("in B") ?{
? ? cout << "B " << s << endl;
? }
? virtual ~B() {}
};
class C {
? M m;
public:
? C(const string& s) : m("in C") ?{
? ? cout << "C " << s << endl;
? }
? virtual ~C() {}
};
class D {
? M m;
public:
? D(const string& s) : m("in D") {
? ? cout << "D " << s << endl;
? }
? virtual ~D() {}
};
class E : public A, virtual public B, virtual public C {
? M m;
public:
? E(const string& s) : A("from E"), B("from E"),
? C("from E"), m("in E") {
? ? cout << "E " << s << endl;
? }
};
class F : virtual public B, virtual public C, public D {
M m;
public:
F(const string& s) : B("from F"), C("from F"),
D("from F"), m("in F") {
cout << "F " << s << endl;
}
};
class G : public E, public F {
M m;
public:
G(const string& s) : B("from G"), C("from G"),
E("from G"), ?F("from G"), m("in G") {
cout << "G " << s << endl;
}
};
int main() {
G g("from main");
} ///:~
//output
M in B
B from G
M in C
C from G
M in A
A from E
M in E
E from G
M in D
D from F
M in F
F from G
M in G
G from main
6、名字查找問題的二義性:
類繼承了兩個同名的函數,沒有方法在他們之間選擇:
lass Top {
public:
? virtual ~Top() {}
};
class Left : virtual public Top {
public:
? void f() {}
};
class Right : virtual public Top {
public:
? void f() {}
};
class Bottom : public Left, public Right {};
int main() {
? Bottom b;
? b.f(); // Error here
} ///:~
消除二義性調用的方法,是以基類名來限定函數的調用。
class Top {
public:
? virtual ~Top() {}
};
class Left : virtual public Top {
public:
? void f() {}
};
class Right : virtual public Top {
public:
? void f() {}
};
class Bottom : public Left, public Right {
public:
? using Left::f;
};
int main() {
? Bottom b;
? b.f(); // Calls Left::f()
} ///:~
名稱優勢:
class A {
public:
? virtual ~A() {}
? virtual void f() { cout << "A::f\n"; }
};
class B : virtual public A {
public:
? void f() { cout << "B::f\n"; }
};
class C : public B {};
class D : public C, virtual public A {};
int main() {
? B* p = new D;
? p->f(); // Calls B::f()
? delete p;
} ///:~
類A是類B的基類,名字B::f比名字A::f占優勢。
7、避免使用多繼承
關心兩個問題:1-是否需要通過新類來顯示兩個類的公共接口。2-需要向上類型轉換成為倆個類型嗎?
第十章 設計模式
動機:為了使變化的事物與不變的事物分離開。
1、模式分類
創建型:Creational:用于怎樣創建一個對象,隔離對象創建的細節,代碼不依賴于對象是什么類型,因此在增加一種新的對象類型時不需要改變代碼。Sington、Factory、Builder模式。
單件模式、工廠模式、構建器模式。
結構型:Structural:影響對象之間的連接方式,確保系統的變化不需要改變對象間的連接。Proxy、Adapter模式。代理模式和適配器模式。
行為型:Behavioral:在程序中處理具有特定操作類型的對象。這些對象封裝要執行的操作過程。如:解釋一種語言、實踐一個請求、遍歷一個序列、實現一個算法等。
Command、Template Method、State、Strategy、Chain of Responsibility、Observer、Multiple Dispatching、Vistor模式
命令模式、模板方法模式、狀態模式、策略模式、職責鏈模式、觀察者模式、多派遣模式、訪問者模式。
2、創建型:單件模式:Singleton ? 它是允許一個類有且僅有一個實例的方法。
#include <iostream>
using namespace std;
class Singleton {
? static Singleton s;
? int i;
? Singleton(int x) : i(x) { }
? Singleton& operator=(Singleton&); ?// Disallowed
? Singleton(const Singleton&); ? ? ? // Disallowed
public:
? static Singleton& instance() { return s; }
? int getValue() { return i; }
? void setValue(int x) { i = x; }
};
Singleton Singleton::s(47);
int main() {
? Singleton& s = Singleton::instance();
? cout << s.getValue() << endl;
? Singleton& s2=Singleton::instance();
? cout<<s2.getValue()<<endl;
? cout<<s.getValue()<<endl;
? s.setValue(10);
? cout<<s2.getValue()<<endl;
? cout<<s.getValue()<<endl;
? s2.setValue(11);
? cout<<s2.getValue()<<endl;
? cout<<s.getValue()<<endl;
} ///:~
//output:
47
47
47
10
10
11
11
1-創建單件模式的關鍵是防止客戶程序員獲得任何控制其對象生存期的權利。聲明所有構造函數為私有??截悩嬙旌瘮岛唾x值函數被聲明為私有。
2-可以采用靜態創建對象,也可以等待,直到客戶程序員提出要求再根據要求進行創建。
3-返回引用而不是返回指針,防止用戶不小心刪除指針。
4-任何情況下,對象應該私有保存。
單件的變體:將在一個成員函數內部的靜態對象的創建與單件類結合在一起。
一個類中的任何static靜態成員對象都表示一個單件:有且僅有一個對象被創建。
如果一個靜態對象依賴于另一個對象,靜態對象的初始化順序是很重要的。
在函數中定義一個靜態對象來控制初始化順序,直到該函數第一次被調用時才進行初始化。如果函數返回一個靜態對象的引用,就可以達到單件的效果。
上面程序修改如下:
#include <iostream>
using namespace std;
class Singleton {
int i;
Singleton(int x) : i(x) { }
Singleton& operator=(Singleton&); ?// Disallowed
Singleton(const Singleton&); ? ? ? // Disallowed
public:
static Singleton& instance() {?
static Singleton ?s(47);
return s;
}
int getValue() { return i; }
void setValue(int x) { i = x; }
};
int main() {
Singleton& s = Singleton::instance();
cout << s.getValue() << endl;
Singleton& s2=Singleton::instance();
cout<<s2.getValue()<<endl;
cout<<s.getValue()<<endl;
s.setValue(10);
cout<<s2.getValue()<<endl;
cout<<s.getValue()<<endl;
s2.setValue(11);
cout<<s2.getValue()<<endl;
cout<<s.getValue()<<endl;
} ///:~
兩個單件彼此依賴:可以很好地控制初始化過程。
class Singleton1{
Singleton1(){cout<<"Singleton1()"<<endl;}
public:
static Singleton1& ref(){
cout<<"前:static Singleton1& ref()"<<endl;
static Singleton1 single;
cout<<"后:static Singleton1& ref()"<<endl;
return single;
}
~Singleton1(){cout<<"~Singleton1()"<<endl;}
};
class Singleton2{
Singleton1 &s1;
Singleton2(Singleton1& s):s1(s){cout<<"Singleton2(Singleton1& s):s1(s)"<<endl;}
public:
static Singleton2& ref(){
cout<<"前:static Singleton2& ref()"<<endl;
static Singleton2 single(Singleton1::ref());
cout<<"后:static Singleton2& ref()"<<endl;
return single;
}
Singleton1 &f(){
cout<<"Singleton1 &f()"<<endl;
return s1;
}
~Singleton2(){cout<<"~Singleton2()"<<endl;}
};
int main(){
Singleton1 &s1=Singleton2::ref().f();
}
//output:
前:static Singleton2& ref()
前:static Singleton1& ref()
Singleton1()
后:static Singleton1& ref()
Singleton2(Singleton1& s):s1(s)
后:static Singleton2& ref()
Singleton1 &f()
~Singleton2()
~Singleton1()
單件的另一種變體:單件角:
MyClass通過下面3個步驟產生一個單件:1-聲明其構造函數為私有或保護的。2-聲明類Singleton<MyClass>為友元。3-從SIngleton<MyClass>派生出MyClass.步驟3:這是對模板Singleton中模板參數的靜態依賴。類Singleton<MyClass>能被編譯器實例化,因為它不依賴MyClass的大小。
#include <iostream>
using namespace std;
template<class T> class Singleton {
? Singleton(const Singleton&);
? Singleton& operator=(const Singleton&);
protected:
? Singleton() {}
? virtual ~Singleton() {}
public:
? static T& instance() {
? ? static T theInstance;
? ? return theInstance;
? }
};
// A sample class to be made into a Singleton
class MyClass : public Singleton<MyClass> {
? int x;
protected:
? friend class Singleton<MyClass>;
? MyClass() { x = 0; }
public:
? void setValue(int n) { x = n; }
? int getValue() const { return x; }
};
int main() {
? MyClass& m = MyClass::instance();
? cout << m.getValue() << endl;
? m.setValue(1);
? cout << m.getValue() << endl;
} ///:~
3、行為型:命令模式(Command):選擇操作
特點:消除代碼間的耦合,消除被調用函數的選擇與那個函數被調用位置之間的聯系。
主要特點:允許向一個函數或者對象傳遞一個想要的動作。
命令模式就是一個函數對象:一個作為對象的函數,通過將函數封裝為對象,就能夠以參數的形式傳遞給其他函數或者對象。
class Command {
public:
? virtual void execute() = 0;
};
class Hello : public Command {
public:
? void execute() { cout << "Hello "; }
};
class World : public Command {
public:
? void execute() { cout << "World! "; }
};
class IAm : public Command {
public:
? void execute() { cout << "I'm the command pattern!"; }
};
// An object that holds commands:
class Macro {
? vector<Command*> commands;
public:
? void add(Command* c) { commands.push_back(c); }
? void run() {
? ? vector<Command*>::iterator it = commands.begin();
? ? while(it != commands.end())
? ? ? (*it++)->execute();
? }
};
int main() {
Macro macro;
macro.add(new Hello);
macro.add(new World);
macro.add(new IAm);
macro.run();
} ///:~
4、結構型:代理模式(Proxy):作為其他對象的前端
代理模式和狀態模式都提供一個代理類,代碼與代理類打交道,而作實際工作的類隱藏在代理類背后。當調用代理類中的一個函數時,代理類轉而去調用實現類中相應的函數。從結構上看,代理模式是狀態模式的一個特例。
基本思想:代理類派生自一個基類,由平行地派生自同一個基類的一個或多個類提供實際的實現。
當一個代理對象被創建的時候,一個實現對象就分配給了它,代理對象就將函數調用發給實現對象。
從結構上看,代理模式和狀態模式的區別:代理模式只有一個實現類,而狀態模式有多個實現。應用也不同:代理模式控制對其實現類的訪問。而狀態模式動態的改變其實現類。
#include <iostream>
using namespace std;
class ProxyBase {
public:
? virtual void f() = 0;
? virtual void g() = 0;
? virtual void h() = 0;
? virtual ~ProxyBase() {}
};
class Implementation : public ProxyBase {
public:
? void f() { cout << "Implementation.f()" << endl; }
? void g() { cout << "Implementation.g()" << endl; }
? void h() { cout << "Implementation.h()" << endl; }
};
class Proxy : public ProxyBase {
? ProxyBase* implementation;
public:
? Proxy() { implementation = new Implementation(); }
? ~Proxy() { delete implementation; }
? // Forward calls to the implementation:
? void f() { implementation->f(); }
? void g() { implementation->g(); }
? void h() { implementation->h(); }
};
int main() ?{
? Proxy p;
? p.f();
? p.g();
? p.h();
} ///:~
5、行為型:狀態模式(State):改變對象的行為
狀態模式產生一個改變其類的對象,當發現在大多數或者所有函數中都存在有條件的代碼時,這種模式很有用。和代理模式一樣,狀態模式通過一個前端對象來使用后端實現對象。
測試一個bool變量。
class Creature {
bool isFrog;
public:
Creature() : isFrog(true) {}
void greet() {
if(isFrog)
cout << "Ribbet!" << endl;
else
cout << "Darling!" << endl;
}
void kiss() { isFrog = false; }
};
在所有操作前都必須測試isFrog變量,使用狀態對象,是代碼簡化。
#include <iostream>
#include <string>
using namespace std;
class Creature {
class State {
public:
virtual string response() = 0;
};
class Frog : public State {
public:
string response() { return "Ribbet!"; }
};
class Prince : public State {
public:
string response() { return "Darling!"; }
};
State* state;
public:
Creature() : state(new Frog()) {}
void greet() {
cout << state->response() << endl;
}
void kiss() {
delete state;
state = new Prince();
}
};
int main() {
Creature creature;
creature.greet();
creature.kiss();
creature.greet();
} ///:~
6、結構型:適配器模式(Adapter)
接受一種類型并且提供一個對其他類型的接口。
創建適配器,接受FibonacciGenerator并產生一個供STL算法使用的迭代器。
class FibonacciGenerator {//斐波那契數列
? int n;
? int val[2];
public:
? FibonacciGenerator() : n(0) { val[0] = val[1] = 0; }
? int operator()() {
? ? int result = n > 2 ? val[0] + val[1] : n > 0 ? 1 : 0;
? ? ++n;
? ? val[0] = val[1];
? ? val[1] = result;
? ? return result;
? }
? int count() { return n; }
};
#include <iostream>
#include <algorithm>
#include <numeric>
#include "FibonacciGenerator.h"
using namespace std;
class FibonacciAdapter { // Produce an iterator
? FibonacciGenerator f;
? int length;
public:
? FibonacciAdapter(int size) : length(size) {}
? class iterator;
? friend class iterator;
? class iterator : public std::iterator<
? ? std::input_iterator_tag, FibonacciAdapter, ptrdiff_t> {
? ? FibonacciAdapter& ap;
? public:
? ? typedef int value_type;
? ? iterator(FibonacciAdapter& a) : ap(a) {}
? ? bool operator==(const iterator&) const {
? ? ? return ap.f.count() == ap.length;
? ? }
? ? bool operator!=(const iterator& x) const {
? ? ? return !(*this == x);
? ? }
? ? int operator*() const { return ap.f(); }
? ? iterator& operator++() { return *this; }
? ? iterator operator++(int) { return *this; }
? };
? iterator begin() { return iterator(*this); }
? iterator end() { return iterator(*this); }
};
int main() {
? const int SZ = 20;
? FibonacciAdapter a1(SZ);
? cout << "accumulate: "
? ? << accumulate(a1.begin(), a1.end(), 0) << endl;
}
7、行為型:模板方法模式(Template Method)
模板方法模式的一個重要特征是它的定義在基類中,并且不能改動。模板方法模式就是堅持相同的代碼,它調用基類的不同函數來驅動程序運行。
驅動程序運行的引擎是模板方法模式,這個引擎是主要的事件環,客戶程序員只需提供customize1()、customize2()的定義,便可以令應用程序運行。
class ApplicationFramework {
protected:
? virtual void customize1() = 0;
? virtual void customize2() = 0;
public:
? void templateMethod() {
? ? for(int i = 0; i < 5; i++) {
? ? ? customize1();
? ? ? customize2();
? ? }
? }
};
// Create a new "application":
class MyApp : public ApplicationFramework {
protected:
? void customize1() { cout << "Hello "; }
? void customize2() { cout << "World!" << endl; }
};
int main() {
? MyApp app;
? app.templateMethod();
} ///:~
8、行為型:策略模式(Strategy):運行時選擇算法
將變化的代碼從“堅持相同代碼”中分開。策略即:使用多種方法來解決某個問題。
好處:在程序運行時可以加入變化的代碼。
class NameStrategy {
public:
? virtual void greet() = 0;
};
class SayHi : public NameStrategy {
public:
? void greet() {
? ? cout << "Hi! How's it going?" << endl;
? }
};
class Ignore : public NameStrategy {
public:
? void greet() {
? ? cout << "(Pretend I don't see you)" << endl;
? }
};
class Admission : public NameStrategy {
public:
? void greet() {
? ? cout << "I'm sorry. I forgot your name." << endl;
? }
};
// The "Context" controls the strategy:
class Context {
NameStrategy& strategy;
public:
Context(NameStrategy& strat) : strategy(strat) {}
void greet() { strategy.greet(); }
};
int main() {
SayHi sayhi;
Ignore ignore;
Admission admission;
Context c1(sayhi), c2(ignore), c3(admission);
c1.greet();
c2.greet();
c3.greet();
} ///:~
9、行為型:職責鏈模式(Chain of Responsibility):嘗試采用一系列策略模式,本質:嘗試多個解決方法直到找到一個起作用的方法。
職責鏈可看做是使用策略對象的遞歸。在職責鏈中,一個函數調用自身,調用函數的一個不同實現,如此反復直至達到某個終止條件,這個終止套件或者是已到達策略鏈的地底部或者是找到一個成功的策略。職責鏈實際上是一個鏈表,動態創建。
使用自動遞歸搜索鏈中每個策略的機制,職責鏈模式自動找到一個解決方法。
#ifndef PURGE_H
#define PURGE_H
#include <algorithm>
template<class Seq> void purge(Seq& c) {
? typename Seq::iterator i;
? for(i = c.begin(); i != c.end(); ++i) {
? ? delete *i;
? ? *i = 0;
? }
}
// Iterator version:
template<class InpIt> void purge(InpIt begin, InpIt end) {
? while(begin != end) {
? ? delete *begin;
? ? *begin = 0;
? ? ++begin;
? }
}
#endif // PURGE_H ///:~
#include <iostream>
#include <vector>
#include "purge.h"
using namespace std;
enum Answer { NO, YES };
class GimmeStrategy {
public:
? virtual Answer canIHave() = 0;
? virtual ~GimmeStrategy() {}
};
class AskMom : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Mooom? Can I have this?" << endl;
? ? return NO;
? }
};
class AskDad : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Dad, I really need this!" << endl;
? ? return NO;
? }
};
class AskGrandpa : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Grandpa, is it my birthday yet?" << endl;
? ? return NO;
? }
};
class AskGrandma : public GimmeStrategy {
public:
? Answer canIHave() {
? ? cout << "Grandma, I really love you!" << endl;
? ? return YES;
? }
};
class Gimme : public GimmeStrategy {
vector<GimmeStrategy*> chain;
public:
Gimme() {
chain.push_back(new AskMom());
chain.push_back(new AskDad());
chain.push_back(new AskGrandpa());
chain.push_back(new AskGrandma());
}
Answer canIHave() {
vector<GimmeStrategy*>::iterator it = chain.begin();
while(it != chain.end())
if((*it++)->canIHave() == YES)
return YES;
// Reached end without success...
cout << "Whiiiiinnne!" << endl;
return NO;
}
~Gimme() { purge(chain); }
};
int main() {
Gimme chain;
chain.canIHave();
} ///:~
10、創建型:工廠模式(Factory):封裝對象的創建。
將創建對象的代碼轉到這個工廠中執行,那么在增加新對象時所做的全部工作就是只需要修改工廠。
實現工廠模式的一種方法就是在基類中定義一個靜態成員函數。
#include <iostream>
#include <stdexcept>
#include <cstddef>
#include <string>
#include <vector>
#include "purge.h"
using namespace std;
class Shape {
public:
virtual void draw() = 0;
virtual void erase() = 0;
virtual ~Shape() {}
class BadShapeCreation : public logic_error {
public:
BadShapeCreation(string type)
: logic_error("Cannot create type " + type) {}
};
static Shape* factory(const string& type)
throw(BadShapeCreation);
};
class Circle : public Shape {
Circle() {} // Private constructor
friend class Shape;
public:
void draw() { cout << "Circle::draw" << endl; }
void erase() { cout << "Circle::erase" << endl; }
~Circle() { cout << "Circle::~Circle" << endl; }
};
class Square : public Shape {
Square() {}
friend class Shape;
public:
void draw() { cout << "Square::draw" << endl; }
void erase() { cout << "Square::erase" << endl; }
~Square() { cout << "Square::~Square" << endl; }
};
Shape* Shape::factory(const string& type)
throw(Shape::BadShapeCreation) {
if(type == "Circle") return new Circle;
if(type == "Square") return new Square;
throw BadShapeCreation(type);
}
char* sl[] = { "Circle", "Square", "Square",
"Circle", "Circle", "Circle", "Square" };
int main() {
vector<Shape*> shapes;
try {
for(size_t i = 0; i < sizeof sl / sizeof sl[0]; i++)
shapes.push_back(Shape::factory(sl[i]));
} catch(Shape::BadShapeCreation e) {
cout << e.what() << endl;
purge(shapes);
return EXIT_FAILURE;
}
for(size_t i = 0; i < shapes.size(); i++) {
shapes[i]->draw();
shapes[i]->erase();
}
purge(shapes);
} ///:~
多態工廠:使用不同類的工廠派生自基本類型的工廠。
工廠模式是多態工廠的一個特例。
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <cstddef>
#include "purge.h"
using namespace std;
class Shape {
public:
? virtual void draw() = 0;
? virtual void erase() = 0;
? virtual ~Shape() {}
};
class ShapeFactory {
? virtual Shape* create() = 0;
? static map<string, ShapeFactory*> factories;
public:
? virtual ~ShapeFactory() {}
? friend class ShapeFactoryInitializer;
? static Shape* createShape(const string& id) {
? ? if(factories.find(id) != factories.end())
? ? ? return factories[id]->create();
? }
};
map<string, ShapeFactory*> ShapeFactory::factories;
class Circle : public Shape {
? Circle() {} // Private constructor
? friend class ShapeFactoryInitializer;
? class Factory;
? friend class Factory;
? class Factory : public ShapeFactory {
? public:
? ? Shape* create() { return new Circle; }
? ? friend class ShapeFactoryInitializer;
? };
public:
? void draw() { cout << "Circle::draw" << endl; }
? void erase() { cout << "Circle::erase" << endl; }
? ~Circle() { cout << "Circle::~Circle" << endl; }
};
class Square : public Shape {
? Square() {}
? friend class ShapeFactoryInitializer;
? class Factory;
? friend class Factory;
? class Factory : public ShapeFactory {
? public:
? ? Shape* create() { return new Square; }
? ? friend class ShapeFactoryInitializer;
? };
public:
? void draw() { cout << "Square::draw" << endl; }
? void erase() { cout << "Square::erase" << endl; }
? ~Square() { cout << "Square::~Square" << endl; }
};
// Singleton to initialize the ShapeFactory:
class ShapeFactoryInitializer {
? static ShapeFactoryInitializer si;
? ShapeFactoryInitializer() {
? ? ShapeFactory::factories["Circle"]= new Circle::Factory;
? ? ShapeFactory::factories["Square"]= new Square::Factory;
? }
public:
~ShapeFactoryInitializer() {
? ? map<string, ShapeFactory*>::iterator it =
? ? ? ShapeFactory::factories.begin();
? ? while(it != ShapeFactory::factories.end())
? ? ? delete it++->second;
? }
};
ShapeFactoryInitializer ShapeFactoryInitializer::si;
char* sl[] = { "Circle", "Square", "Square",
? "Circle", "Circle", "Circle", "Square" };
int main() {
? vector<Shape*> shapes;
? ? for(size_t i = 0; i < sizeof sl / sizeof sl[0]; i++)
? ? ? shapes.push_back(ShapeFactory::createShape(sl[i]));
? for( i = 0; i < shapes.size(); i++) {
? ? shapes[i]->draw();
? ? shapes[i]->erase();
? }
? purge(shapes);
} ///:~
//output:
Circle::draw
Circle::erase
Square::draw
Square::erase
Square::draw
Square::erase
Circle::draw
Circle::erase
Circle::draw
Circle::erase
Circle::draw
Circle::erase
Square::draw
Square::erase
Circle::~Circle
Square::~Square
Square::~Square
Circle::~Circle
Circle::~Circle
Circle::~Circle
Square::~Square
抽象工廠:使用若干工廠方法模式,每個工廠方法模式創建一個不同類型的對象。
class Obstacle {
public:
? virtual void action() = 0;
};
class Player {
public:
? virtual void interactWith(Obstacle*) = 0;
};
class Kitty: public Player {
? virtual void interactWith(Obstacle* ob) {
? ? cout << "Kitty has encountered a ";
? ? ob->action();
? }
};
class KungFuGuy: public Player {
? virtual void interactWith(Obstacle* ob) {
? ? cout << "KungFuGuy now battles against a ";
? ? ob->action();
? }
};
class Puzzle: public Obstacle {
public:
? void action() { cout << "Puzzle" << endl; }
};
class NastyWeapon: public Obstacle {
public:
? void action() { cout << "NastyWeapon" << endl; }
};
// The abstract factory:
class GameElementFactory {
public:
? virtual Player* makePlayer() = 0;
? virtual Obstacle* makeObstacle() = 0;
};
// Concrete factories:
class KittiesAndPuzzles : public GameElementFactory {
public:
? virtual Player* makePlayer() { return new Kitty; }
? virtual Obstacle* makeObstacle() { return new Puzzle; }
};
class KillAndDismember : public GameElementFactory {
public:
? virtual Player* makePlayer() { return new KungFuGuy; }
? virtual Obstacle* makeObstacle() {
? ? return new NastyWeapon;
? }
};
class GameEnvironment {
? GameElementFactory* gef;
? Player* p;
? Obstacle* ob;
public:
? GameEnvironment(GameElementFactory* factory)
? : gef(factory), p(factory->makePlayer()),
? ? ob(factory->makeObstacle()) {}
? void play() { p->interactWith(ob); }
? ~GameEnvironment() {
? ? delete p;
? ? delete ob;
? ? delete gef;
? }
};
int main() {
? GameEnvironment
? ? g1(new KittiesAndPuzzles),
? ? g2(new KillAndDismember);
? g1.play();
? g2.play();
}
/* Output:
Kitty has encountered a Puzzle
KungFuGuy now battles against a NastyWeapon */ ///:~
虛函數的思想就是發送一個消息給對象,讓對象確定要做正確的事情。
11、創建型:構建器模式(Builder)
目標:將對象的創建與它的表示法分開。構建器模式和抽象工廠模式主要的區別就是,構建器模式一步步創建對象,
功能:將部件組合成為一個完整的產品的算法和部件本身分開,這樣就允許通過一個共同借款的不同實現來為不同的產品提供不同的算法。
12、行為型:觀察者模式(Observer)
解決一個常見問題:當某些其他對象改變狀態時,應該如何處理。
在觀察著模式中有兩個變化的事件,正在進行觀察的對象的數量和更新發生的方式。即觀察著模式允許修改這二者而不影響周圍的其他代碼。
#ifndef OBSERVER_H
#define OBSERVER_H
class Observable;
class Argument {};
class Observer {
public:
? // Called by the observed object, whenever
? // the observed object is changed:
? virtual void update(Observable* o, Argument* arg) = 0;
? virtual ~Observer() {}
};
#endif // OBSERVER_H ///:~
類Obervable只有一個成員函數:update()接口類。當正在被觀察的對象認為到了更新其所有觀察者的時機時,它將調用此函數。它允許被觀察者的對象傳遞引起更新操作的對象和任何額外的信息。
#ifndef OBSERVABLE_H
#define OBSERVABLE_H
#include <set>
#include "Observer.h"
class Observable {
? bool changed;
? std::set<Observer*> observers;
protected:
? virtual void setChanged() { changed = true; }
? virtual void clearChanged() { changed = false; }
public:
? virtual void addObserver(Observer& o) {
? ? observers.insert(&o);
? }
? virtual void deleteObserver(Observer& o) {
? ? observers.erase(&o);
? }
? virtual void deleteObservers() {
? ? observers.clear();
? }
? virtual int countObservers() {
? ? return observers.size();
? }
? virtual bool hasChanged() { return changed; }
? // If this object has changed, notify all
? // of its observers:
? virtual void notifyObservers(Argument* arg = 0) {
? ? if(!hasChanged()) return;
? ? clearChanged(); // Not "changed" anymore
? ? std::set<Observer*>::iterator it;
? ? for(it = observers.begin();it != observers.end(); it++)
? ? ? (*it)->update(this, arg);
? }
? virtual ~Observable() {}
};
#endif // OBSERVABLE_H ///:~
13、行為型:多重派遣模式(Multiple dispatching)
好處:在調用的時候能夠以簡潔的句法表達方式達到預期的效果。
多態只能通過虛函數調用來實現,要發生多重派遣,必須有一個虛函數調用以確定每個未知的類型。
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <ctime>
#include <cstdlib>
#include "purge.h"
using namespace std;
class Paper;
class Scissors;
class Rock;
enum Outcome { WIN, LOSE, DRAW };
ostream& operator<<(ostream& os, const Outcome out) {
? switch(out) {
? ? default:
? ? case WIN: return os << "win";
? ? case LOSE: return os << "lose";
? ? case DRAW: return os << "draw";
? }
}
class Item {
public:
? virtual Outcome compete(const Item*) = 0;
? virtual Outcome eval(const Paper*) const = 0;
? virtual Outcome eval(const Scissors*) const= 0;
? virtual Outcome eval(const Rock*) const = 0;
? virtual ostream& print(ostream& os) const = 0;
? virtual ~Item() {}
? friend ostream& operator<<(ostream& os, const Item* it) {
? ? return it->print(os);
? }
};
class Paper : public Item {
public:
? Outcome compete(const Item* it) { return it->eval(this);}
? Outcome eval(const Paper*) const { return DRAW; }
? Outcome eval(const Scissors*) const { return WIN; }
? Outcome eval(const Rock*) const { return LOSE; }
? ostream& print(ostream& os) const {
? ? return os << "Paper ? ";
? }
};
class Scissors : public Item {
public:
? Outcome compete(const Item* it) { return it->eval(this);}
? Outcome eval(const Paper*) const { return LOSE; }
? Outcome eval(const Scissors*) const { return DRAW; }
? Outcome eval(const Rock*) const { return WIN; }
? ostream& print(ostream& os) const {
? ? return os << "Scissors";
? }
};
class Rock : public Item {
public:
? Outcome compete(const Item* it) { return it->eval(this);}
? Outcome eval(const Paper*) const { return WIN; }
? Outcome eval(const Scissors*) const { return LOSE; }
? Outcome eval(const Rock*) const { return DRAW; }
? ostream& print(ostream& os) const {
? ? return os << "Rock ? ?";
? }
};
struct ItemGen {
? Item* operator()() {
? ? switch(rand() % 3) {
? ? ? default:
? ? ? case 0: return new Scissors;
? ? ? case 1: return new Paper;
? ? ? case 2: return new Rock;
? ? }
? }
};
struct Compete {
? Outcome operator()(Item* a, Item* b) {
? ? cout << a << "\t" << b << "\t";
? ? return a->compete(b);
? }
};
int main() {
? srand(time(0)); // Seed the random number generator
? const int sz = 20;
? vector<Item*> v(sz*2);
? generate(v.begin(), v.end(), ItemGen());
? transform(v.begin(), v.begin() + sz,
? ? v.begin() + sz,
? ? ostream_iterator<Outcome>(cout, "\n"),
? ? Compete());
? purge(v);
} ///:~
//output:
003807A8 ? ? ? ?00382A10 ? ? ? ?1
003807E0 ? ? ? ?00382A48 ? ? ? ?0
00380818 ? ? ? ?00382A80 ? ? ? ?2
00382658 ? ? ? ?00382AB8 ? ? ? ?0
00382690 ? ? ? ?00382AF0 ? ? ? ?0
003826C8 ? ? ? ?00382B28 ? ? ? ?1
00382700 ? ? ? ?00382B60 ? ? ? ?1
00382738 ? ? ? ?00382B98 ? ? ? ?2
00382770 ? ? ? ?00382BD0 ? ? ? ?2
003827A8 ? ? ? ?00382C08 ? ? ? ?2
003827E0 ? ? ? ?00382C40 ? ? ? ?1
00382818 ? ? ? ?00382C78 ? ? ? ?0
00382850 ? ? ? ?00382CB0 ? ? ? ?1
00382888 ? ? ? ?00382CE8 ? ? ? ?0
003828C0 ? ? ? ?00382D20 ? ? ? ?2
003828F8 ? ? ? ?00382D58 ? ? ? ?2
00382930 ? ? ? ?00382D90 ? ? ? ?1
00382968 ? ? ? ?00382DC8 ? ? ? ?2
003829A0 ? ? ? ?00382E00 ? ? ? ?1
003829D8 ? ? ? ?00382E38 ? ? ? ?2
virtual Item::compete函數開始雙重派遣。函數compete調用eval執行第二次派遣。
14、行為型:訪問者模式(Visitor)
目標:將類繼承層次結構上的操作與這個層次結構本身分開。訪問者模式建立在雙重派遣方案之上。訪問者模式允許創建一個獨立的類層次結構Visitor而有效的對主類的借口進行擴展。
#include <algorithm>
#include <iostream>
#include <typeinfo>
#include <string>
#include <vector>
#include <ctime>
#include <cstdlib>
#include "purge.h"
using namespace std;
class Gladiolus;
class Renuculus;
class Chrysanthemum;
class Visitor {
public:
? virtual void visit(Gladiolus* f) = 0;
? virtual void visit(Renuculus* f) = 0;
? virtual void visit(Chrysanthemum* f) = 0;
? virtual ~Visitor() {}
};
class Flower {
public:
? virtual void accept(Visitor&) = 0;
? virtual ~Flower() {}
};
class Gladiolus : public Flower {
public:
? virtual void accept(Visitor& v) {
? ? v.visit(this);
? }
};
class Renuculus : public Flower {
public:
? virtual void accept(Visitor& v) {
? ? v.visit(this);
? }
};
class Chrysanthemum : public Flower {
public:
? virtual void accept(Visitor& v) {
? ? v.visit(this);
? }
};
// Add the ability to produce a string:
class StringVal : public Visitor {
? string s;
public:
? operator const string&() { return s; }
? virtual void visit(Gladiolus*) {
? ? s = "Gladiolus";
? }
? virtual void visit(Renuculus*) {
? ? s = "Renuculus";
? }
? virtual void visit(Chrysanthemum*) {
? ? s = "Chrysanthemum";
? }
};
// Add the ability to do "Bee" activities:
class Bee : public Visitor {
public:
? virtual void visit(Gladiolus*) {
? ? cout << "Bee and Gladiolus" << endl;
? }
? virtual void visit(Renuculus*) {
? ? cout << "Bee and Renuculus" << endl;
? }
? virtual void visit(Chrysanthemum*) {
? ? cout << "Bee and Chrysanthemum" << endl;
? }
};
struct FlowerGen {
? Flower* operator()() {
? ? switch(rand() % 3) {
? ? ? default:
? ? ? case 0:?
?cout<<"new Gladiolus"<<endl;
?return new Gladiolus;
? ? ? case 1:?
? cout<<"new Renuculus"<<endl;
? return new Renuculus;
? ? ? case 2:
? cout<<"new Chrysanthemum"<<endl;
? return new Chrysanthemum;
? ? }
? }
};
int main() {
? srand(time(0)); // Seed the random number generator
? vector<Flower*> v(10);
? generate(v.begin(), v.end(), FlowerGen());
? cout<<endl;
? vector<Flower*>::iterator it;
? // It's almost as if I added a virtual function
? // to produce a Flower string representation:
? StringVal sval;
? for(it = v.begin(); it != v.end(); it++) {
? ? (*it)->accept(sval);
? ? cout << " string(sval)"<<string(sval) << endl;
? }
? // Perform "Bee" operation on all Flowers:
? Bee bee;
? for(it = v.begin(); it != v.end(); it++)
?(*it)->accept(bee);
? purge(v);
} ///:~
//output:
new Chrysanthemum
new Chrysanthemum
new Renuculus
new Renuculus
new Gladiolus
new Gladiolus
new Gladiolus
new Renuculus
new Renuculus
new Chrysanthemum
?string(sval)Chrysanthemum
?string(sval)Chrysanthemum
?string(sval)Renuculus
?string(sval)Renuculus
?string(sval)Gladiolus
?string(sval)Gladiolus
?string(sval)Gladiolus
?string(sval)Renuculus
?string(sval)Renuculus
?string(sval)Chrysanthemum
Bee and Chrysanthemum
Bee and Chrysanthemum
Bee and Renuculus
Bee and Renuculus
Bee and Gladiolus
Bee and Gladiolus
Bee and Gladiolus
Bee and Renuculus
Bee and Renuculus
Bee and Chrysanthemum
Flower是主層次結構,Flower的各個子類通過函數accept()得到一個Visitor,Flower主層次結構除了函數accept沒有別的操作,因此Flower層次結構的所有功能都將包含在Visitor層次結構中。每個Flower中的accept函數開始一個雙重派遣。第一次派遣決定了Flower類型,第二次派遣決定了Visitor類型?
第十一章
1、進程是在其自己的地址空間運行的自含式程序。線程是進程內的單一連續的控制流。因此一個進程可以有多個并發執行的線程。線程運行在一個進程內,所以它們共享內存和其他資源。
2、防止兩個線程在臨界區訪問同一資源,加鎖,互斥。 在進入臨界區之前獲得互斥鎖,在臨界區的終點釋放。
3、原子操作:1-返回int型變量。2-原子操作不能被線程處理機制中斷。3-對int型變量增1操作也是原子操作。
4、線程狀態:1-新建狀態。2-可運行狀態。3-阻塞狀態。4-死亡狀態。
5、變為阻塞狀態:原因:1-調用sleep()使線程處于休眠狀態。2-已經使用wait()掛起了該線程的運行,在得到signal()或broadcast()消息之前,它不會變為可執行狀態。3-線程正在等待某個I/O操作完成。4-線程正在嘗試進入一段被一個互斥鎖保護的代碼塊。
6、除了I/O操作,一個任務可以從任何阻塞操作中中斷出來。
7、進程間協作,通過互斥鎖來同步兩個任務的行為,來阻止一個任務干擾另一個任務的資源。
8、死鎖發生的條件:1-互斥。線程使用的資源至少有一個必須是不可共享的。2-至少有一個進程必須持有某一種資源。并且同時等待獲得正在被另外的進程所持有的資源。3、不能以搶占的方式剝奪一個進程的資源。4、出現一個循環等待。一個進程等待另外的進程所持有的資源,而這個被等待的進程又等待另一個進程所持有的資源。。。
破壞死鎖的最容易的方法是破壞4.
9、5個哲學家就餐問題:每個哲學家以特定的順序拿筷子:先右后左。進入循環等待。解決方法:最后一個哲學家初始化為:先嘗試拿左邊的筷子,再拿右邊的筷子。
10、線程的優點:提供輕量級(100條指令)的執行語境切換,而非重量級(上千條指令)進程語境切換。進程中所有的線程共享同一內存空間。一個輕量級的語境切換只改變了程序執行的先后順序和局部變量。進程改變-重量級語境切換-必須調換所有內存空間。
總結
以上是生活随笔為你收集整理的C ++ 编程思想(卷二) 笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: openldap schema
- 下一篇: Centos 安装 OpenLDAP