Effective C++學習第八天

條款26:盡可能延后變量定義式的出現時間

? ? ? ? ? 當你定義了一個變量,如果在使用變量之前出現異常,那么你得承受一次構造成本和析構成本,而且你沒有使用該變量;本條款給出的建議是延遲變量的定義,直到非得使用該變量的前一刻為止,甚至應該嘗試延后這份定義知道能夠給它初值實參為止;這樣不僅可以避免構造(析構)非必要的對象,還可以避免無意義的default構造行為;

條款27:盡量少做轉型動作

? ? ? ? ? 常見C風格的類型轉換:(T)expression? ? ? ? ? ? ? ? ?函數風格的轉型? ?T(expression)? ? ? //舊式轉型

? ? ? ? ? C++提供的四種新式轉型:

? ? ? ? ? ? ? ? const_cast<T>(expression);//去除對象常量性;

? ? ? ? ? ? ? ? dynamic_cast<T>(expression);//對象安全向下轉型,用于繼承

? ? ? ? ? ? ? ? reinterpret_cast<T>(expression);

? ? ? ? ? ? ? ? static_cast<T>(expression);//隱式轉型

盡量使用新式轉型方式:1)代碼容易辨識;2)各轉型動作的目標比較窄,編譯器容易診斷;

? ? ? ? ? 轉型并不是告訴編譯器把某種類型視為另一種類型,任何一個類型轉換(不論是通過轉型操作而進行的顯式轉換或通過編譯器完成的隱式轉換)往往真的令編譯器編譯出運行期間執行的代碼;

? ? ? ? ? 對于一個base class指針指向一個derived class對象,有時候上述的兩個指針值并不相同,它們之間會有一個偏移量在運行期間施加在derived*指針上來得到正確的base*指針值;轉型代碼錯誤分析:

class window{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?class specialwindow:public window{

public:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?public:

? ? ? ? ? ?virtual void onresize( ){...}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? virtual void onresize( ){

}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?static_cast<window>(*this).onresize( );//解決方法

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//window::onresize();

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}

原本想著采用static_cast將派生類對象轉化為基類對象執行onresize函數,然后再執行派生類的onresize函數;但實際上是在當前對象的副本上執行了window::onresize,在當前對象上執行是specialwindow專屬動作(個人理解:相當于轉型可以當做為創建一個副本執行了一次函數調用的過程);

對于dynamic_cast:用于你相對一個derived class執行derived class操作函數,但是你的手上只有一個指向base的pointer或者reference,你只能通過dynamic_cast向下尋找你的derived class對象,然而使用dynamic_cast會用到strcmp比較class名稱,使得運行效率極低;改善這個問題的方法有兩種:

? ? ? ? ? 1)使用容器并在其中存儲直接指向derived class對象指針(通常是智能指針),如此便消除了通過base class接口處理對象的需要(但這種情況下,你需要多個容器分別儲存不同派生類的指針,而且每個容器都具備類型安全性),具體代碼如下:

class window {...};

class specialwindow::public window{

public:

? ? ? ? ? ? ? ?void blink( );

};

typedef std::vector<std::trl:shared_ptr<specialwindow>vpsw;

vpsw winptrs;

for(vpsw::iterator iter=winptrs.begin( );iter!=winptrs.end( );iter++)

? ? ? ? ? ? ? ? ? (*it)->blink( );

? ? ? ? ? ? 2)通過base class接口處理所有可能之各種window派生類,也就是提供virtual函數;

條款28:避免返回handles指向對象內部成分

? ? ? ? ? ?對于常見的pimpl設計方法(即數據和實現分離),有兩個需要注意的地方:1)成員變量的封裝性最多等于返回你reference的函數的訪問級別;2)const成員函數傳出來一個reference,后者(reference)所指數據與對象自身有關聯,而它又被儲存于對象之外,那么函數調用者可以修改那筆數據;

? ? ? ? ? ?絕對不要令成員函數返回一個指針指向訪問級別較低的成員函數,如果你那么做,后者的訪問級別就會被提高到較高者,因為客戶可以去的一個指向訪問級別更低的函數,然后通過那個指針去調用它;

? ? ? ? ? ?如果有一個handle(成員函數返回reference,指針或者迭代器)被傳出去,那么就可以用這個handle訪問對象的數據,對象的封裝性也就下降了;

? ? ? ? ?避免返回handles(reference,指針,迭代器)指向對象內部,遵守這個規定,可以增加封裝性,幫助const成員函數更像const,并將發生“虛吊號碼牌(指針或引用指向不存在的對象)”的可能性降到最低;

條款29:為“異常安全”而努力是值得的

? ? ? ? ?當異常拋出時,帶有異常安全性的函數會:1)不泄露資源;2)不允許數據敗壞;

? ? ? ? ?異常安全函數提供一下三個保證中的一個:1)基本承諾;異常被拋出,程序內的任何事情仍然保持在有效的狀態下,沒有任何對象或者數據結構會因此而敗壞,所有對象都處于一種內部前后一致的狀態;2)強烈保證:如果異常被拋出,程序狀態不改變。調用這樣的函數需有這樣的認知,如果函數成功,就是完全成功,如果函數失敗,程序會回復到“調用函數之前的狀態”,調用一個提供強烈保證的函數后,程序狀態只有兩種可能,如預期般到達函數成功執行后的狀態或者回到函數被調用前的狀態。3)不拋擲保證,承諾不拋擲異常,因為它們總能完成它們原先承諾的功能;

? ? ? ? ?強烈保證往往能夠以copy-and-swap實現出來,但是強烈保證并非對所有的函數都有實現或者具備現實意義,一般基本承諾就可以了;

? ? ? ?函數提供的“異常安全性”通常最高只等于其所調用之各個函數的“異常安全性保證”中的最弱者。

條款31:透徹了解inlining的里里外外

? ? ? ? ? ?inlining函數的好處:產生較小的目標碼,調用它們不需要承受函數調用所招致的額外開銷;缺點是會導致程序體積過大(虛內存現象)inline造成代碼膨脹導致額外的換頁行為,降低高速緩存器裝置的擊中率;?

? ? ? ? ?inline只是對編譯器的一個申請,不是強制命令,編譯器可以忽略;這項申請可以隱喻提出,也可以明確指出(加inline關鍵字)。隱喻的方式是將函數定義在class定義式內;

? ? ? ? inline函數通常被放置在頭文件中,在編譯階段實現對函數本體的替換;編譯器拒絕將復雜(循環或者遞歸)、virtual函數函數指針調用的情況下將函數變成inline;

? ? ? ? inline函數在隨著程序庫的升級時無法升級,同時不支持設置端點調試(本身不是函數);

? ? ? ?結論:將大多數inline函數限制在小型、被頻繁調用的函數身上,這可以使得日后調試過程和二進制升級更容易,也可以使得潛在的代碼膨脹最小化,使程序的速度提升最大化;

? ? ? 不要只因為function templates出現在頭文件,將它們聲明為inline;

條款31:將文件間的編譯依存關系降至最低

? ? ? ? 在設計對象的過程中,我們可以將對象分割為兩個class,一個只提供接口(數據),一個負責實現該接口(函數實現),這就是所謂的pimpl方式;這種設計方式的好處是:1)在修改數據代碼時,我們只需要修改接口數據就行,而不要修改實現;2)在函數聲明時,當其中有class時,我們只需要聲明class而不需要定義class;具體設計策略如下:

? ? ? ? ? ? ? 如果使用object reference或者object pointers可以完成任務,就不要使用objects;

? ? ? ? ? ? ? 如果能夠,盡量以class聲明式替換class定義式,這樣可以省去調用構造函數的開銷;

? ? ? ? ? ? ?為聲明式和定義式提供不同的頭文件;

? ?上述的方法稱為handle class 的構造方法,常見的handle class的構造方法除了有pimpl方式,還有一種特殊的抽象基類(interface class),這種class的目的是調試derived class 接口,因此通常不帶成員變量,沒有構造函數,只有一個虛析構函數和一組pure virtual函數,如:

class person{

public:

? ? ? ? ? ?virtual ~person( );

? ? ? ? ? ?virtual std::string name( ) const=0;

? ? ? ? ? ?virtual std::string birthdata( ) const=0;

? ? ? ? ? virtual std::string address( )const=0;

};

interface class自己創建單個對象,這樣的函數稱為factory函數或virtual構造函數,它們返回指針(智能指針),指向動態分配所得對象,而該對象支持interface class接口,通常函數被聲明為static;代碼如下:

class person{

public:

? ? ? ? ?static std::trl::shared_ptr<person>(create(const std::string&name,const Date&birthday,const address&addr);

};

? ? ? ? ? ?Handle class和interface class解除了接口和實現之間的耦合關系,從而降低文件間的編譯依存性;對于handle class,成員函數必須通過impl pointer取得對象數據,每一次訪問就會增加一層間接性,同時每一個對象消耗的內存數量必須增加impl pointer的大小,最后impl pointer必須初始化,指向一個動態分配的impl object,因此可能會帶來bad alloc異常;interface class每次調用只付出一個間接跳躍成本,此外派生的interface class中必須含有一個vptr,這個指針會增加存放對象所需的內存數量。

? ? ? ? 在程序開發中,如果使用handle class和interface class的實現碼在速度或大小差異大于類之間的耦合時,可以用具體類代替handle class和interface class;

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

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

相關文章

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…

函數sscanf小結

1.sscanf用于處理固定格式的字符串&#xff0c;包含在頭文件<cstdio>中&#xff0c;函數原型為&#xff1a; int sscanf(const char *buffer,const char*format,[]argument ]...); 其中:buffer代表著要存儲的數據&#xff0c;format 代表格式控制字符串&#xff0c;arg…

redis源碼剖析(六)—— Redis 數據庫、鍵過期的實現

文章目錄數據庫的實現數據庫讀寫操作鍵的過期實現數據庫的實現 我們先看代碼 server.h/redisServer struct redisServer{...//保存 db 的數組redisDb *db;//db 的數量int dbnum;... }再看redisDb的代碼&#xff1a; typedef struct redisDb {dict *dict; /*…