Effective C++學習第七天

條款23:寧以non-memeber、non-friend替換member函數

? ? ? ? ? non-member/non-friend可以給對象帶來更大的封裝性,從兩個方面來考慮:1)考慮封裝,越多東西被封裝,它們就越不可見,就越少人看到它,就會越有彈性去改變它;2)考慮對象內的數據,越少代碼可以看到數據,越多的數據被封裝,那么我們就越能自由地改變對象數據;

? ? ? ? ? 在C++中,可以讓所有的non-member函數放在同一個命名空間內,然后使用那個命名空間;C++標準程序庫并不是單一、整體、龐大的頭文件,而是很多頭文件組合而成的,這就允許客戶只對他們所有的那部分系統形成編譯相依;

條款24:若所有參數都需類型轉換,用non-member代替member函數

? ? ? ? ? ? ?對于參數都允許發生隱式轉換的函數,使用non-member函數可以使你的類功能更加具有一致性,而且還支持混合式算術編程;具體代碼分析如下:

? ? ? ? ?const rational operator*(const rational&rhs)const{

? ? ? ? ? ? ? ?return rational(this->numerator( )*rhs.numerator( ),this->denominator( )*rhs.denominator( );

}

rational onehalf(1,2);

rational result=2*onehalf;//錯誤,調用實際情況是2.operator(&2,onehalf);//不能對常量取地址操作

rational result=onehalf*2;//正確

修改為non-member函數

const rational operator*(const rational& lhs ,? const rationa & rhs){

? ? ? return rational(lhs.numerator( )*rhs.numerator( ),lhs.denominator( )*rhs.denominator( );

}

結論:如果你需要為某個函數的所有參數(this指針所指的那個隱喻參數)進行類型轉換,那么這個函數必須是個non-member;

條款25:考慮寫出一個不拋異常的swap函數

? ? ? ? ? ? ?swap原本是STL中的一部分,后成為異常安全性編程的脊柱以及用來處理自我賦值可能的一種常見機制;

?1.缺省情況下,std標準庫的swap算法:

? ? ? ? ? ? ? ? ? template<typename T>

? ? ? ? ? ? ? ? ? void swap(T&a,T&b){T temp(a);a=b;b=temp;}//實現是基于T的copy構造函數和copy assignment完成

2.通常情況下,數據的表現形式都是“以指針指向一個對象,內含真正的數據”,這種設計模式通常變現為pimpl(pointer to implementation)手法,如:

class widgetimpl{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? class widget{

private:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?private:

? ? ? ? ? int a ,b,c;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?widgetimpl*pimpl;

? ? ? ? ? std::vector<double>v;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? };

}

如果需要交換兩個widget對象,那么我們唯一需要做的就是交換兩個pimpl指針,但是普通的swap算法卻是復制了三次widgetimpl;

解決上述問題的一個方法:就是將std::swap針對widget全特化(模板函數的一個實例),然后用widget的成員函數調用它(寫錯了)用全特化的swap函數來調用public swap成員函數,具體代碼如下:

class widget{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?namespace std{

public:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? template<>//針對widget的特化版本

? ? ? ? ? void swap(widget&other){? ? ? ? ? ? ? ? ? ? ? ? ? ?void swap<widget>(widget&a.widget&b){

? ? ? ? ? using std::swap;//讓std內的swap可用? ? ?? ? ? ? ? ? ? ? ? ? ? ?a.swap(b);? ? ? ? ? ? //調用swap成員函數;

? ? ? ? ?swap(pimpl,other.pimpl);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }

};? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}

3.如果widget和widgetpimpl都是class templates而非class,那么我們重新定義非成員函數;

? ? ? ?template<typename T>

? ? ? void swap<widget<T>>(widget<T>&a,widget<T>&b){ a.swap(b);}? ? ? ?//不合法,錯誤

原因是我們企圖對function template偏特化,但是C++只允許對class template偏特化;

引申:std是一個特殊的命名空間,其管理規則也比較特殊,客戶可以全特化(實例化)std內的template,但是不可以添加新的templates(class或者templates或者其他的任何東西)到std里頭,C++禁止這類行為,但是編譯器卻不會報錯,但是軟件可能會出現不可預期的行為;

? ? 解決上述問題的一個辦法:添加一個重載函數來代替我們要做的偏特化一個function template行為,我們還是聲明一個non-member swap函數讓他調用member swap,但不再聲明將那個non-member swap聲明為std::swap的特化版本,具體實現代碼如下:

?template<typename T>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???template<typename T>

class widget{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?void swap(T&obj1,T&obj2){

public:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?using std::swap;

? ? ? ? void swap(widget&other){? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?swap(obj1,obj2);//T型對象的最佳調用swap版本

? ? ? ??? using std::swap;? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}? ?//優先調用T專屬版本,即public swap成員函數? ? ? ? ? ? ? ? ? ??

? ? ? ? ?swap(pimpl,other.pimpl);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//并在該版本不存在的情況下調用std內一般化的版本

}

結論:1)如果swap缺省實現碼可以實現你的class或者class template提供可接受的效率,你不需要做任何事情;

? ? ? ? ? 2)如果swap缺省碼不夠實現你的效率,你可以:

? ? ? ? ? ? ? ?a)提供一個public swap成員函數,讓它處理兩個對象值;

? ? ? ? ? ? ? ?b)在你所在的class或者template所在的命名空間內提供一個non-member swap,并令它調用上述swap成員函數;

? ? ? ? ? ? ? ?c)如果你在編寫一個class(而非class template),為你的class特化std::swap,并令它調用你的swap成員函數。

? ? ? ? 3)如果你調用swap,確定包含一個using聲明式,以便std::swap在你的函數內曝光,然后不加namespace,單純調用swap(使swap函數有更多的選擇);

成員版的swap絕不可能拋出異常,因為swap的一個最好應用就是幫助classes提供強烈的異常安全性保障(以copy構造函數和copy assignment操作符為基礎的);

引申:關于模板的全特化和偏特化描述,可以參考博文:https://www.cnblogs.com/yyehl/p/7253254.html


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/382388.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/382388.shtml
英文地址,請注明出處:http://en.pswp.cn/news/382388.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis運維和開發學習筆記(5) 主從復制和sentinel哨兵模式

Redis運維和開發學習筆記(5) 主從復制和sentinel哨兵模式 主從復制 將主節點的數據改變同步給從節點 作用 備份數據讀寫分離 存在的問題&#xff1a; 手動干預切主等操作主節點的寫能力受到單機限制主節點的存儲能力受到單機限制 主從模式的故障恢復 當主節點發生故障時&am…

Effective C++學習第八天

條款26&#xff1a;盡可能延后變量定義式的出現時間當你定義了一個變量&#xff0c;如果在使用變量之前出現異常&#xff0c;那么你得承受一次構造成本和析構成本&#xff0c;而且你沒有使用該變量&#xff1b;本條款給出的建議是延遲變量的定義&#xff0c;直到非得使用該變量…

Redis運維和開發學習筆記(6) 監控Redis工作狀態-info命令

Redis運維和開發學習筆記(6) 監控Redis工作狀態-info命令 文章目錄Redis運維和開發學習筆記(6) 監控Redis工作狀態-info命令info serverinfo clientinfo memoryinfo persistenceinfo statsinfo commandstatsinfo cpuinfo clusterinfo keyspaceinfo server Redis服務器相關的通用…

Effective C++學習第九天

條款32&#xff1a;確定你的public繼承塑模出is-a模型class D&#xff08;derived&#xff09;以public形式繼承class B&#xff08;base&#xff09;&#xff0c;則每一個類型為D的對象同時也是一個類型為B的對象&#xff0c;反之不成立&#xff0c;因此B比D表現出更加一般化的…

Effective C++學習第十天

條款36&#xff1a;絕不重新定義繼承而來的non-virtual函數non-virtual函數執行的是靜態綁定&#xff0c;在編譯器就已經決定&#xff0c;因此對象對用的函數只和指針的類型有關&#xff0c;而與指針所指的對象無關&#xff1b;記住non-virtual函數的性質&#xff1a;不變性凌駕…

Redis運維和開發學習筆記(7) 內存管理和過期策略

Redis運維和開發學習筆記(7) 內存管理和過期策略 文章目錄Redis運維和開發學習筆記(7) 內存管理和過期策略內存回收策略惰性刪除定時任務刪除maxmemory過期策略allkeys-lru主從搭建測試搭建完畢主從測試結果volatile-lru測試結果volatile-ttl測試結果allkeys-lru內存回收策略 …

Effective C++學習第十一天

條款41&#xff1a;了解隱式接口和編譯期多態面向對象編程世界總是以顯式接口&#xff08;源碼可見的接口&#xff09;和運行期多態&#xff08;virtual&#xff09;解決問題&#xff1b;對于templates及泛型編程的世界&#xff0c;隱式接口和編譯期多態顯得更加重要&#xff1…

Redis源碼分析(零)學習路徑筆記

文章目錄第一階段第二階段 熟悉Redis的內存編碼結構第三階段 熟悉Redis數據類型的實現第四階段 熟悉Redis數據庫的實現第五階段 熟悉客戶端和服務器端的代碼實現第六階段 這一階段主要是熟悉Redis多機部分的代碼實現關于測試方面的文件有一些工具類的文件如下SORT命令的實現一些…

Effective C++學習第十二天

條款47&#xff1a;請使用traits classes表現類型信息STL有五類迭代器分類&#xff0c;input迭代器&#xff08;只讀&#xff0c;一次&#xff0c;向前&#xff09;、output迭代器&#xff08;可寫&#xff0c;一次&#xff0c;向前&#xff09;、forward迭代器&#xff08;讀/…

Redis源碼分析(一)redis.c //redis-server.c

Redis源碼分析&#xff08;一&#xff09;redis.c //redis-server.c 入口函數 int main() 4450 int main(int argc, char **argv) {4451 initServerConfig();4452 if (argc 2) {4453 ResetServerSaveParams();4454 loadServerConfig(argv[1]);4455 …

Linux 學習

1.linux文本命令行語言環境設置命令 查看當前語言環境&#xff1a; echo &#xffe5;LANG 修改&#xff1a; LANG選擇的語言環境’ 引申&#xff1a;https://blog.csdn.net/huoyunshen88/article/details/41113633 2.linux中的硬鏈接和軟連接 linux中文件的儲存方式&#xf…

vivo C/C++工程師視頻面試總結 20180802

1.自我介紹&#xff1a;有點兒緊張了&#xff0c;直接把自己簡歷上的一些信息信息說了一遍&#xff0c;說完之后在介紹了一下自己的平時的愛好和興趣&#xff0c;感覺面試官沒有理我&#xff0c;直接進入下一環節了。 2.項目詳情&#xff1a;主要是自己最近的一個項目和自己負…

Redis源碼分析(二)redis-cli.c

文章目錄1. int main()2. parseOptions(int argc, char **argv) 進行ip和port的改變3. lookupCommand(char *name) 查找命令&#xff0c;判斷命令合法3.2 strcasecmp(name,cmdTable[j].name)3.1 redisCommand cmdTable[]4. cliSendCommand(int argc, char **argv)4.1 cliConnec…

C語言中有bool變量嗎?

1.C/C中定義的數據類型&#xff1a; C語言中定義了6種基本數據類型&#xff1a;short,int,long,float,double,char 4種構造類型&#xff1a;數組&#xff0c;結構體&#xff08;struct&#xff09;&#xff0c;共用類型(union)&#xff0c;枚舉類型(enum) 指針類型和空類型 C語…

redis源碼剖析(三)——基礎數據結構

文章目錄SDS鏈表字典這篇文章關于 Redis 的基礎數據:SDS SDS &#xff08;Simple Dynamic String&#xff09;是 Redis 最基礎的數據結構。直譯過來就是”簡單的動態字符串“。Redis 自己實現了一個動態的字符串&#xff0c;而不是直接使用了 C 語言中的字符串。 sds 的數據結…

C++迭代器使用錯誤總結

指針和迭代器的區別&#xff1a; 迭代器&#xff1a; &#xff08;1&#xff09;迭代器不是指針&#xff0c;是類模板&#xff0c;表現的像指針。他只是模擬了指針的一些功能&#xff0c;通過重載了指針的一些操作符&#xff0c;->,*, --等封裝了指針&#xff0c;是一…

redis源碼剖析(四)跳表

文章目錄整數集合跳躍表壓縮列表總結整數集合 當一個集合只包含整數&#xff0c;且這個集合的元素不多的時候&#xff0c;Redis 就會使用整數集合 intset 。首先看 intset 的數據結構&#xff1a; typedef struct intset {// 編碼方式uint32_t encoding;// 集合包含的元素數量…

vivo C/C++工程師 HR視頻面試問題總結20180807

一開始沒想到這次視頻面是HR面試&#xff0c;還以為是技術面試&#xff0c;畢竟上次面試的時候技術問題問的相對比較少&#xff0c;所以面試準備方向有點兒錯了&#xff0c;不過還是總結一下具體問題。 1&#xff09;自我介紹&#xff1a;吸取了上次自我介紹的經驗&#xff0c;…

在Redis客戶端設置連接密碼 并演示密碼登錄

我們先連接到Redis服務 然后 我們要輸入 CONFIG SET requirepass “新密碼” 例如 CONFIG SET requirepass "A15167"這樣 密碼就被設置成立 A15167 我們 輸入 AUTH 密碼 例如 AUTH A15167這里 返回OK說明成功了 然后 我們退出在登錄就真的需要 redis-cli -h IP地…

redis源碼剖析(五)—— 字符串,列表,哈希,集合,有序集合

文章目錄對象REDIS_STRING &#xff08;字符串&#xff09;REDIS_LIST 列表REDIS_SET &#xff08;集合&#xff09;REDIS_ZSET &#xff08;有序集合&#xff09;REDIS_HASH (hash表)int refcount&#xff08;引用計數器&#xff09;unsigned lru:REDIS_LRU_BITS對象 對于 Re…