Effective C++ 類與函數(shù)設(shè)計(jì)和申明
Effective C++ 類與函數(shù)的設(shè)計(jì)和申明
在看《Effective C++》這本書的過程中,我無數(shù)次的發(fā)出感嘆,這他媽寫得太好了,句句一針見血,直接說到點(diǎn)上。所以決定把這本書的內(nèi)容加上自己的理解寫成5篇博客,我覺得不管你是否理解這些條款,都值得你先記下來。下面的索引對應(yīng)的是書中的章節(jié)。
18:努力讓接口完美且最小化
19:區(qū)分member functions,non-member functions和friend functions三者
20:避免將data member放在公開接口中
21:盡量使用const
22:盡量使用 pass-by-refernece,少用pass-by-value
18:努力讓接口完美且最小化
為了客戶端的方便調(diào)用,接口中可能會定義很多方法,而其中可能右很多方法是多余或是重復(fù)的,這樣會導(dǎo)致接口中方法太多,讓用戶迷失在一堆的方法中,而且大型接口不易維護(hù),長長的class定義導(dǎo)致頭文件很長,會增大編譯的時(shí)間。但是也不必太過吝嗇方法的個(gè)數(shù),如果加入一個(gè)member function會是class更好用,會是增加一個(gè)member function能減少客戶端的錯(cuò)誤,那都是這些方法都是成為接口一份子的理由。
19:區(qū)分member functions,non-member functions和friend functions三者
member function可以是虛函數(shù)而non-member function不可以,如果一個(gè)函數(shù)必須是動態(tài)綁定的那么他就必須是虛函數(shù),就必須是memberfunction,虛函數(shù)能實(shí)現(xiàn)動態(tài)綁定是因?yàn)樽宇惪梢愿鶕?jù)自己的需要重寫父類的虛方法實(shí)現(xiàn)動態(tài)綁定,而non-member function不可能被重寫。Friend function是獨(dú)立于class的,他只是可以訪問class的私有成員,如果一個(gè)方法不需要訪問一個(gè)class的私有成員,就不應(yīng)該讓這個(gè)方法稱為這個(gè)類的friend function。
class Rational { public: Rational(int numerator=0,int denominator=1); int numerator()const; int denominator() const; const Rational operator*(const Rational& rhs)const; private: ... };
上面這個(gè)類表示一個(gè)分?jǐn)?shù),分?jǐn)?shù)的加減乘除的方法都沒有提供,那我們該以什么樣的方式實(shí)現(xiàn)這些操作呢,是member function還是non-member function還是friend function呢?
第一直覺就是這些操作是屬于Rational的應(yīng)該是member function,那么我們就新增一個(gè)關(guān)于乘法的public member function,就是下面這個(gè)樣子:
const Rational operator*(const Rational& rhs)const;
簡單的介紹為什么是這個(gè)樣子。先解釋3個(gè)const,第一個(gè)const表示方法的返回為const,就是禁止我們對一個(gè)乘法賦值,如禁止a*b=3;第二個(gè)const表示在這個(gè)方法中不能修改rhs中任何成員的值,第三個(gè)const表示這個(gè)方法是const方法,在這個(gè)方法中不能修改調(diào)用這個(gè)方法的對象的數(shù)據(jù)成員。Const還有其他很多作用將會在下一個(gè)條款中介紹。
返回值為什么是by value?首先我們必須用一個(gè)變量來存乘法的結(jié)果值,我們不能在方法中構(gòu)造一個(gè)局部變量,然后返回他的引用,因?yàn)檫@個(gè)方法執(zhí)行完后,局部變量會被自動回收。
參數(shù)為什么為引用類型?一句話盡量用by reference代替by value,條款22專門講述這個(gè)問題。這個(gè)返回值返回使用by value是沒有其他辦法了,你必須用一個(gè)變量來存放結(jié)果值。有了這個(gè)方法我們就可以進(jìn)行乘法操作了。
Rational oneHalf(1,2),twoFive(2,5);
Rational result=oneHalf*twoFive;//沒有問題
Result=oneHalf*3;//沒有問題,在類型不匹配的時(shí)候編譯器會一直尋找隱式類型轉(zhuǎn)換的方法,直到找不到報(bào)錯(cuò),由于構(gòu)造函數(shù)的兩個(gè)參數(shù)都有默認(rèn)值,所以可以發(fā)生隱式類型轉(zhuǎn)換,3相當(dāng)于Rational(3,1),于是不會出現(xiàn)任何問題。
乘法的交換律告訴我們:a*b=b*a;于是我想oneHalf*3可以寫成3*oneHalf,但是對不起不行,
3*oneHalf相當(dāng)于3.operator(oneHalf),在這個(gè)3是操作對象,不會發(fā)生任何類型轉(zhuǎn)換,而oneHalf是參數(shù),于是編譯器尋找將Rational轉(zhuǎn)換成int(假設(shè)3為int類型)的方法,當(dāng)然是沒有啦。為了實(shí)現(xiàn)Rational和int類型的任意操作用member function是不可能啦,于是用non-member function,于是寫下乘法的方法如下:
const Rational operator*(const Rational& lhs,const Rational& rhs) { return Rational(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs. denominator()); }
Non-member function當(dāng)然不需要用cosnt修飾,將乘法結(jié)果存在一個(gè)匿名變量中,如果編譯有優(yōu)化的話,這個(gè)結(jié)果將直接存在接收這個(gè)方法的變量中。有了這個(gè)方法,3*oneHalf就沒有任何問題了,現(xiàn)在3被隱式轉(zhuǎn)換成Rational(3,1)了,不會出現(xiàn)任何問題,如果你不想出現(xiàn)隱式類型轉(zhuǎn)換,就在構(gòu)造函數(shù)的前面加上explicit。最后要考慮的是是否需要將這個(gè)方法稱為Rational的friend function,當(dāng)然是不需要,因?yàn)樽屗Q為friend function沒有任何幫助,多一個(gè)沒有任何幫助的朋友有這個(gè)必要嗎?
20:避免將data member放在公開接口中
將data member設(shè)為private,然后用member function實(shí)現(xiàn)讀寫操作,對于用過面向?qū)ο笳Z言的朋友都知道,就不廢話了。如果這些方法只是返回data member的話,可以讓這些方法稱為inline,就可以節(jié)省方法調(diào)用帶來的性能損失。
21:盡量使用const
關(guān)于這一條上面也提到一些。
Const修飾方法的返回值表示不可以直接對這個(gè)方法進(jìn)行賦值;
Const修飾方法表示在這個(gè)方法中不能修改data member;
Const修飾參數(shù)表示這個(gè)參數(shù)在這個(gè)方法中不能修改;
常量性的不同也可以實(shí)現(xiàn)方法的重載,常量對象只能調(diào)用對應(yīng)的常量方法。非常量對象可以調(diào)用常量方法。
22:盡量使用 pass-by-refernece,少用pass-by-value
先看一個(gè)類,然后看兩個(gè)方法的對比,其他的廢話就多說了,因?yàn)镃#引用類型默認(rèn)的是pass-by-reference,而C++任何類型默認(rèn)的都是pass-by-value。
class DataItem { public: DataItem() { cout<<" constructor DataItem"<<endl; } ~DataItem() { cout<<" ~destructor DataItem"<<endl; } DataItem(const DataItem& item) { cout<<" constructor DataItem"<<endl; value=item.value; text=item.text; //*this=item;//這句的作用等同于上面兩句,但是它會調(diào)用operator= ,就多了一次方法調(diào)用 } const DataItem& operator=(const DataItem& item) { cout<<" operator= DataItem"<<endl; text=item.text; value=item.value; return *this; } DataItem* operator&() { return this; } const DataItem* operator&()const { return this; } int GetValue() { return value; } void SetValue(int val) { value=val; } string& GetText() { return *text; } void SetText(string* txt) { text=txt; } private : int value; string* text; };
兩個(gè)對比方法及測試代碼:
DataItem getDataItemByValue(DataItem item) { return item; } const DataItem& getDataItemByReference(const DataItem& item) { return item; } void TestDataItem() { DataItem item; cout<<"getDataItemByValue start:"<<endl; getDataItemByValue(item); cout<<"getDataItemByValue end"<<endl; cout<<endl; cout<<"getDataItemByReference start:"<<endl; getDataItemByReference(item); cout<<"getDataItemByReference end"<<endl; }
結(jié)果截圖:

從結(jié)果中我們看到pass-by-value多調(diào)用兩次構(gòu)造函數(shù),兩次析構(gòu)函數(shù),還有對象的數(shù)據(jù)成員的構(gòu)造和析構(gòu),損失的確是很慘重。
廢話真的不想多少了,寫這種博客真他媽累,有不想寫的念頭了,還是把時(shí)間放在實(shí)戰(zhàn)練習(xí)中吧。
Effective c++這個(gè)系列暫時(shí)暫停。
Effective C++系列:
Effective C++構(gòu)造函數(shù)析構(gòu)函數(shù)Assignment運(yùn)算符
Effective C++ 類與函數(shù)的設(shè)計(jì)和申明
作者:陳太漢
浙公網(wǎng)安備 33010602011771號