讀書筆記_Effective_C++_條款四十三:學習處理模板化基類的名稱

背景是這樣的,有兩個不一樣的公司,而後想設計一個MessageSender,爲這兩個公司發送不一樣的消息,既支持明文發送SendClearText,也支持密文發送SendEncryptedText。一種思路是採用動態綁定的方法,定義一個BasicMessageSender,裏面有兩個方法,分別是發送明文和密文的虛函數,而後定義它的子類MessageSenderForCompanyA,以及MessageSenderForCompanyB,在這兩個子類裏面覆蓋發送明文和密文的虛函數,從而達到根據不一樣公司發送不一樣消息的目的。函數

但這裏咱們想換一種思路,使用靜態多態的方法來實現,靜態多態就是模板的技術了,代碼以下:this

 1 class CompanyA
 2 {
 3 public:
 4     void SendClearText(){}
 5     void SendEncryptedText(){}
 6 };
 7 
 8 
 9 class CompanyB
10 {
11 public:
12     void SendClearText(){}
13     void SendEncrypedText(){}
14 };
15 
16 template <class T>
17 class MsgSender
18 {
19 public:
20     void SendClearText(){}
21 };
22 
23 template <class T>
24 class MsgSenderWithLog: public MsgSender<T>
25 {
26 public:
27     void SendClearTextWithLog()
28     {
29         // Logs
30         SendClearText(); // 有的編譯器會編不過這段代碼
31     }
32 };
33 
34 int main()
35 {
36     MsgSenderWithLog<CompanyA> MsgSender;
37     MsgSender.SendClearTextWithLog();
38 }

CompanyA與CompanyB有各自的發送函數,而後有一個模板類MsgSender,這個模板待肯定的參數是T,能夠是CompanyA或者CompanyB,這樣就能夠在定義MsgSender時,好比MsgSender<CompanyA>或者MsgSender<CompanyB>,指定到底調用的哪一個公司的發送函數了,這是在編譯期就能夠肯定下來的事情。spa

但如今有一個新問題,那就是咱們但願在執行發送函數以前,仍是加上日誌比較好,這樣咱們就繼承了MsgSender,定義了一個新類MsgSenderWithLog,在這裏定義了一個新的函數SendClearTextWithLog,在這個函數裏面調用了父類的SendClearText。設計

對於這段代碼,其實思路仍是挺清晰的,但問題是有的編譯器會編不過這行代碼(VS2008之後的版本的都是能夠的,以前的版本沒試),爲何?日誌

 

這是由於在模板技術中存在全特化的概念,好比C公司,這個公司根本不想發送明文,也就是說它只有SendEncryptedText()接口,沒有SendClearText()。爲了使咱們的靜態多態仍然可用,咱們這樣定義只適用於C公司的MsgSender:code

 1 class CompanyC
 2 {
 3 public:
 4     void SendEncryptedText(){}
 5 };
 6 
 7 template <>
 8 class MsgSender<CompanyC>
 9 {
10 public:
11     void SendEncryptedText(){}
12 };

這時候若是去調用:blog

1 MsgSenderWithLog<CompanyC> MsgSenderC;
2 MsgSenderC.SendClearTextWithLog(); //編譯器沒法經過編譯

這樣編譯器會報找到SendClearText()的錯。正是由於有的編譯器考慮到了全特化模板版本能夠與普通版本不一樣,因此在有繼承關係存在時,對直接調用父類的函數給出了不支持的error。但這個error是與編譯器相關的,不是必然出現的。繼承

爲了讓更多的編譯器放棄這種全特化的憂慮,書上提供了三種解決方法:接口

方法一: 將編譯器

 1 void SendClearTextWithLog()
 2 {
 3     // Logs
 4     SendClearText(); // 有的編譯器會編不過這段代碼
 5 }
 6 改爲
 7 void SendClearTextWithLog()
 8 {
 9     // Logs
10     this->SendClearText(); // 這下能編譯經過了
11 }

方法二:

在子類中聲明using MsgSender<T>::SendClearText;

編譯器報error本質是不進行模板父類域的查找,因此這裏using了父類的一個函數名,強制編譯器對之進行查找。

 

方法三:

將SendClearText()指明爲MsgSender<T>::SendClearText()。

 

最後總結一下:

可在derived class template內經過「this->」指涉base class templates內的成員名稱,或藉由一個明白寫出的「base class資格修飾符」完成。

相關文章
相關標籤/搜索