Effective C++學習第十一天

條款41:了解隱式接口和編譯期多態

? ? ? ? 面向對象編程世界總是以顯式接口(源碼可見的接口)和運行期多態(virtual)解決問題;對于templates及泛型編程的世界,隱式接口編譯期多態顯得更加重要;

? ? ? ? 對于template,只有當參數具體確定時(具現化)才能確定具體操作,而這些具現化的行為發生在編譯期,以不同的template參數具現化function templates會導致調用不同的函數,這就是所謂的編譯期多態

? ? ? ? ?通常顯示接口由函數的簽名式(函數名稱,參數類型,返回類型)構成,而隱式接口則不基于函數簽名式,它是由有效表達式組成,表達式自身看起來很復雜,但它們要求的約束條件一般而言直接而又明確;

? ? ? ? template參數身上的隱式接口,和class對象上的顯示接口,都是在編譯器完成檢查;

? ? ? ? 對template參數而言,接口是隱式的,基于有效表達式;多態則是通過template具現化和函數重載解析發生于編譯器

條款42:了解typename的雙重意義

? ? ? ? ? 在聲明template類型參數時,class和typename意義完全相同;

? ? ? ? ? 引申:template內出現的名稱如果相依于某個template參數,稱之為從屬名稱,如果從屬名稱在class內呈嵌套狀,我們稱它為嵌套從屬名稱,如c::const_iterator就是這樣的名稱,它其實是一個嵌套從屬類型名稱,也就是個嵌套從屬名稱并指涉某類型;如果一個名稱(int)并不依賴于template參數的名稱,這樣的名稱稱為謂非從屬名稱;代碼如下:

template<typename c>

void print2nd(const c&container)

{

? ? ? ? ? ?if(container.size()>=2){

? ? ? ? ? ? ?c::const_iterator iter(container.begin( ));

? ? ? ? ? ? ? ++iter;

? ? ? ? ? ? ? int value=*iter;

? ? ? ? ? ?}

}

? ? ? ? 嵌套從屬名稱會導致編譯困難,如c::const_iterator *p;C++解析代碼有一個規定:如果解析器在template中遭遇一個嵌套從屬名稱,它便假設這個名稱不是個類型,除非你告訴它是,所以缺省情況下,嵌套從屬名稱不是類型(有一個例外);正確的情況就是告訴C++解析器這個一個類型,在名稱前加關鍵字typename就可以了;

? ? ? ?typename只被用來驗明嵌套從屬類型名稱,其他名稱不該有它的存在;typename作為嵌套從屬類型名稱的前綴詞有一個例外:typename不可以出現在base classes list內的嵌套從屬結構,也不可在member initialization list(成員初始列表)中作為base class修飾符,如:

template <typename T>

class derived:public base<T>::Nested{

public:

? ? ? ? explicit derived(int x):base<T>::Nested(x){

? ? ? ? ? typename base<T>::Nested temp;

? ? ? ? }

};

條款43:學習處理模塊化基類內的名稱

? ? ? ? ? ? ? 考慮如下的程序代碼:

template <typename company>? ? ? ? ? ? ? ? ? ? ? ? ? ?template<typename company>

class msgsender{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? class loggingmsgsender:public msgsender<company>{

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

? ? ? ?void sendclear(const msginfo & info)? ? ? ? ? ? ? ? void sendclearmsg(const msginfo & info){

? ? ? {

? ? ? ? ? std::string msg;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sendclear(info);//調用base class函數,代碼編譯不通過

? ? ? ? ?company c;

? ? ? ? c.sendcleartext(msg);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}

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

};

? ? ? 分析:當編譯器遇到class template loggingmsgsender定義式,并不知道它繼承什么樣的class,當然它繼承的是msgsender<company>,但其中的company是個template參數,不到后來(loggingmsgsender被具現化)無法確切知道它是什么,也就不知道是否有sendclear這個函數;C++編譯器拒絕調用sendclear的原因:它知道base classtemplates有可能被特化,而那個特化的版本不提供和一般性template相同的接口

? ? ? 解決C++不進入templatized base classes的方法有三種:1)在base class函數調用之前加上this->,也就是this->sendclear(info);2)使用using聲明式,告訴編譯器sendclear在base class內,如using msgsender<company>::sendclear;3)明確指出被調用函數位于base class內,如msgsender<company>::sendclear(info);但是如果調用的是virtual函數,那么就會關閉virtual綁定行為;

條款44:將與參數無關的代碼抽離templates

? ? ? ? ?templates是節省時間和避免代碼重復的一個奇方妙法,但是使用templates可能會導致代碼膨脹,其二進制碼帶著重復的代碼數據;可以通過使用共性和變性分析的方法解決這個問題,將多個class的共同部分搬離到新的class,并通過繼承或復合的方式來得到公共屬性;

? ? ? ? ?任何templates代碼都不該與某個造成膨脹的templates參數產生相依的關系;

? ? ? ? ?因非類型模板參數而造成的代碼膨脹,往往可消除,做法是以函數參數或class成員變量替換templates參數,如矩陣類求逆實現;

? ? ? ? ?因類型參數而造成的代碼膨脹,往往可以降低,做法是讓帶有完全相同二進制表述的具現類型共享實現代碼,如int和long實現;

? ? ? ?如果你實現某些成員函數而他們操作強型指針(如T*),你應該令他們調用另一個操作無類型指針(void *)的函數,后者完成實際工作;

條款45:運用成員函數模板接受所有兼容類型

? ? ? ? ? ?真實指針支持隱式轉換,如derived class隱式轉換成base class;但是同一個templates的不同具現體之間并不存在什么與生俱來的固有關系(如某個帶有base-derived關系的B,D兩類型分別具現某個template,產生的兩個具現體并不帶有base-derived關系);

? ? ? ? ? ?C++給我們提供一個稱為member function templates,它為模板類提供構造函數;這一類構造函數通過對象u創建對象t,而u和t的類型是同一個template的不同具現體,我們稱這個函數為泛化構造函數,如:

template<typename T>

class smartptr{

public:

? ? ? ?template<typename u>

? ? ? smartptr(const smartptr<u>& other);

};

? ? ? ? ?但是對于泛化構造函數,有時我們需要指定隱式轉換的方向,例如我們不能將int *轉換為double*,這時候我們可能需要一個指針指向class的原始數據,這樣問題就變成只有存在兩個底層指針可以轉換的才是我們需要的結果;

? ? ? ? member function templates(成員函數模板)的效果不限于構造函數,它還支持賦值操作,如shared_ptr類的代碼:

template<typename T>

class shared_ptr{

public:

? ? template<class Y>

? ? ?explicit shared_ptr(Y* p);//沒有const,原因???

? ? ?template<class Y>

? ? ?shared_ptr(shared_ptr<Y>const&r);//沒有explicit,允許隱式轉換

? ? ?template<class Y>

? ? ?explicit shared_ptr(weak_ptr<Y>const&r);

? ? template<class Y>

? ? ?explicit shared_ptr(auto_ptr<Y>&r);//沒有const,auto_ptr性質決定

? ? template<class Y>

? ? ?shared_ptr&operator=(shared_ptr<Y>const &r);

? ? template<class Y>

? ? ?shared_ptr&operator=(auto_ptr<Y>&r);//沒有const,auto_ptr性質決定

};

? ? ?member function templates會聲明一個泛化的copy構造函數和copy assignment操作符,但是它不會改變C++語言的規則,C++語言規定如果程序需要一個copy構造函數,你沒有聲明,編譯器會自動幫你生成一個,相同規則也適用于賦值操作,因此如果你不想讓系統聲明正常的拷貝構造函數和賦值操作符,你得自己聲明;

條款46:需要類型轉換時請為模板定義非成員函數

? ? ? ? ?當不存在模板時,如果函數參數都存在隱式轉換,則需要將這個函數定義成非成員函數;當存在模板時,這條規則又發生了變化,編譯無法通過,因為在template具現化實參推導過程中從不將隱式類型轉換函數納入考慮;

? ? ? ? 解決的方法:利用friend關鍵字,將非成員函數聲明為友元函數(新用法),同時提供實現(實現在函數外,通過友元函數調用,這樣可以實現代碼沖擊最小化)(如果只是單純提供友元聲明,那么可以通過編譯,卻沒有辦法完成連接,這種稱為混合式代碼調用,代碼調用成功的原因是:函數在被調用的過程中參數可以實現隱式變換;

? ? ? ?類模板的聲明式為:template<typename T> class rational;? ? ?

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

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

相關文章

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; /*…

多益網絡 視頻面試面試總結20180816

1.首先是自我介紹&#xff1a;因為等了半個小時&#xff0c;所以有點兒緊張&#xff0c;只說了一下自己的學校&#xff0c;愛好和興趣&#xff1b; 2.介紹了一個自己的最成功的項目&#xff1a;我介紹了一個關于GPS導航的項目&#xff0c;介紹了項目的內容和項目的一些工作&am…

redis源碼剖析(七)—— Redis 數據結構dict.c

文章目錄dict.hdict.cdict.h //定義錯誤相關的碼 #define DICT_OK 0 #define DICT_ERR 1//實際存放數據的地方 typedef struct dictEntry {void *key;void *val;struct dictEntry *next; } dictEntry;//哈希表的定義 typedef struct dict {//指向實際的哈希表記錄(用數組開鏈的…

簡述linux中動態庫和靜態庫的制作調用流程

假設現在有這些文件&#xff1a;sub.c add.c div.c mul.c mainc head.h&#xff08;前4個.C文件的頭文件&#xff09; 1.靜態庫制作流程 gcc -c sub.c add.c div.c mul.c -->生成 .o目標文件文件 ar rcs libmycal.a *.o …

redis源碼剖析(八)—— 當你啟動Redis的時候,Redis做了什么

文章目錄啟動過程初始化server結構體main函數會調用initServer函數初始化服務器狀態載入持久化文件&#xff0c;還原數據庫開始監聽事件流程圖啟動過程 初始化server結構體從配置文件夾在加載參數初始化服務器載入持久化文件開始監聽事件 初始化server結構體 服務器的運行ID…

linux中錯誤總結歸納

1.使用gcc編譯C文件&#xff0c;C文件在for循環語句中出現變量定義 編譯器提示錯誤&#xff1a;“for”loop initial declarations are only allowed in C99 mode. note:use option -stdc99or-stdgnu99 to compile; 原因&#xff1a;gcc的標準是基于c89的&#xff0c;c89不能在…