Effective C++笔记_条款43 学习处理模板化基类内的名称

开篇就来了一个示例代码,整个这个小节就围绕这个示例代码来描述模板化基类内的名称(函数)。主要是因为C++知道base class templates有可能被特化,而那个特化版本肯呢个不提供和一般性template相同的接口。因此它往往拒绝在templatized base classes(模板化基类)内寻找继承而来的名称。

 1 class CompanyA {
 2  public:
 3      //...
 4      void sendCleartext(const std::string& msg);
 5      void sendEncrypted(const std::string& msg);
 6      //...
 7  };
 8
 9  class CompanyB {
10  public:
11      //...
12      void sendCleartext(const std::string& msg);
13      void sendEncrypted(const std::string& msg);
14      //...
15  };
16
17  //...  针对其他公司设计的类
18
19  /*用来保存信息,以备将来产生信息*/
20  class MsgInfo {
21      //...
22  };
23
24 template<typename Company>
25 class MsgSender {
26 public:
27     //... 构造函数、析构函数等
28     void sendClear(const MsgInfo& info)
29     {
30         std::string msg;
31         // 根据info产生信息
32         Company c;
33         c.sendCleartext(msg);
34     }
35
36     void sendSecret(const MsgInfo& info)
37     {
38         std::string msg;
39         // 根据info产生信息
40         Company c;
41         c.sendEncryptedtext(msg);
42     }
43 };
44
45 /* 日志:记录每个信息发送相关记录 */
46 template<typename Company>
47 class LoggingMsgSender:public MsgSender<Company> {
48     //... 构造函数、析构函数等
49     void sendClearMsg(const MsgInfo& info)
50     {
51
52         // 将“传送前”的信息写到log
53         sendClear(info);             // 调用base class函数 (error:不同通过编译)
54         // 将“传送后”的信息写到log
55     }
56
57 };
58
59 /*调用base class函数 (error:不同通过编译)的原因:
60   模板类型在编译前不知道Company是否有sendClear函数,比如下面这个 CompanyZ class
61  */
62 class CompanyZ {
63     // 不提供sendClearText函数
64     //...
65      void sendEncrypted(const std::string& msg);
66      //...
67 };

为了解决上述问题:可以使用为 CompanyZ,搞个绿色通道:
  1.class 定义式前面加 template<> 表明:它既不是template也不是标准的class,而是一个特化版的MsgSender template,在template实参是CompanyZ时被使用。
  2.模板全特化(total template specialization):template MsgSender针对类型CompanyZ特化了,而且其特化是全面性的,也就说一旦类型参数被指定为CompanyZ,再没有其他template参数可供变化。

1 template<>            // 一个全特化的MsgSender,与一般template相同,差别只在于它删掉了sendClear
2 class MsgSender<CompanyZ> {
3     //...
4     void sendSecret(const MsgInfo& info)
5     {
6         sendEncryptedtext(info);
7     }
8 };
 1 /* 再来看,日志:记录每个信息发送相关记录 */
 2 template<typename Company>
 3 class LoggingMsgSender:public MsgSender<Company> {
 4     //... 构造函数、析构函数等
 5     void sendClearMsg(const MsgInfo& info)
 6     {
 7
 8         // 将“传送前”的信息写到log
 9         sendClear(info);
10         /*
11             如果Company == CompanyZ, CompanyZ不存在sendClear函数,报错。
12             原因:编译器知道base class templates有可能被特化,而那个特化版本肯呢个不提供
13             和一般性template相同的接口。因此它往往拒绝在templatized base classes(模板化基类)内寻找继承而来的名称。
14         */
15         // 将“传送后”的信息写到log
16     }
17
18 };


让C++“不进入templatized base classes观察”的行为失效的解决方法如下:

  1. 在 base class 函数调用动作之前加上 “this->”:

 1 /* 日志:记录每个信息发送相关记录 */
 2 template<typename Company>
 3 class LoggingMsgSender:public MsgSender<Company> {
 4     //... 构造函数、析构函数等
 5     void sendClearMsg(const MsgInfo& info)
 6     {
 7
 8         // 将“传送前”的信息写到log
 9         this->sendClear(info);
10         // 将“传送后”的信息写到log
11     }
12     //...
13 };


2.使用using声明式
  这里并不是base class名称被derived class名称遮掩,而是编译器不进入base class作用域内查找,于是通过using告诉它,请它那么做。

 1 /* 日志:记录每个信息发送相关记录 */
 2 template<typename Company>
 3 class LoggingMsgSender:public MsgSender<Company> {
 4     using MsgSender<Company>::sendClear;            // 告诉编译器,清它假设sendClear位于base class内
 5     //... 构造函数、析构函数等
 6     void sendClearMsg(const MsgInfo& info)
 7     {
 8
 9         // 将“传送前”的信息写到log
10         sendClear(info);
11         // 将“传送后”的信息写到log
12     }
13     //...
14 };


3.明确指出被调用的函数位于base class内
  缺点:如果被调用的是virtual函数,明确自个修饰(explicit qualification) 会关闭"virtual绑定行为"

 1 /* 日志:记录每个信息发送相关记录 */
 2 template<typename Company>
 3 class LoggingMsgSender:public MsgSender<Company> {
 4     //... 构造函数、析构函数等
 5     void sendClearMsg(const MsgInfo& info)
 6     {
 7
 8         // 将“传送前”的信息写到log
 9         MsgSender<Company>::sendClear(info);
10         // 将“传送后”的信息写到log
11     }
12     //...
13 };


总结:

  1. 三种方法都是从名称可视点(visibility point)的角度出发:

  对编译器承诺“base class template”的任何特化版本都将支持其一般(泛化)版本所提供的接口。[编译器的早期诊断时间:当解析derived class template的定义式时];如果这个承诺没有被实践,往后的编译器最终会给事实一个公道。[在编译器晚期诊断时间:当那些templates被特定的template具体化时]。如下面的示例:

1  LoggingMsgSender<CompanyZ> zMsgSender;
2  MsgInfo msgData;
3  //...
4  zMsgSender.sendClearMsg(msgData);   // error

  2.可在derived class templates内通过"this->"指涉base class templates内的成员名称,或藉由一个明白写出的“base class资格修饰符”完成。



声明:全文文字都是来源《Effective C++》 3th。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。

时间: 2024-08-28 20:23:12

Effective C++笔记_条款43 学习处理模板化基类内的名称的相关文章

Effective C++ Item 43 学习处理模板化基类内的名称

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:可在derived class templates 内通过 "this->" 指涉 base class templates 内的成员名称,或藉由一个明白写出的 "base class 资格修饰符"完成. 示例: class CompanyA{ public: //... void sendCleartext(const std::string &

《Effective C++》:条款43:学习处理模板化基类内的名称

[toc] 模板化的类作为基类时,有哪些要注意的地方.以一个例子说明,假设现在编写一个发送信息到不同公司的程序,信息要么译成密码,要么就是原始文字,在编译期间来决定哪一家公司发送至哪一家公司,采用template手法: class CompanyA{ public: void sendCleartext(const std::string& msg); void sendEncryted(const std::string& msg); -- }; class CompanyB{ publ

Effective C++:条款43:学习处理模板化基类内的名称

(一) 注意从 "面向对象的C++" 转向 "模板C++" 时继承可能遭遇问题 :由于基类模板可能被特化,而该特化版本可能会改变成员,因此C++拒绝在模板化基类中寻找继承而来的名称. (二) 看下面的例子: 假设将信息传送到不同的公司去,传送方式包括明文传送和密文传送,采用模板类的设计方法: class CompanyA { public: ... void sendClearText(const string& msg); void sendEncrypt

学习处理模板化基类内的名称

1.从面向对象的C++转向模板C++时继承可能遭遇问题:由于基类模板可能被特化,而该特化版本肯可能会改变成员,因此C++拒绝在模板基类中寻找继承而来的名称 2.实例:假设信息传送到不同的公司去,传送方式包括明文传送和密文传送,采用模板类的设计方法: template<typename Company> class MsgSender{ public: ... void sendClear(const MsgInfo& info){ std::string msg; ... Compan

读书笔记 effective c++ Item 43 了解如何访问模板化基类中的名字

1. 问题的引入——派生类不会发现模板基类中的名字 假设我们需要写一个应用,使用它可以为不同的公司发送消息.消息可以以加密或者明文(未加密)的方式被发送.如果在编译阶段我们有足够的信息来确定哪个信息会被发送到哪个公司,我们可以使用基于模板的解决方案: 1 class CompanyA { 2 public: 3 ... 4 void sendCleartext(const std::string& msg); 5 void sendEncrypted(const std::string&

Effective C++笔记_条款46 需要类型转换时请为模板定义非成员函数

看这个章节的时候又跑回去看了一下 条款 24,本章的例子就是基于下面这个例子而来的.在 item 24 中,支持混合运算的示例代码如下: 1 class Rational { 2 public: 3 Rational(int numerator = 0, int denominator = 1); 4 int mumerator() const; 5 int denominator() const; 6 private: 7 int numerator; 8 int denominator; 9

Effective C++笔记_条款35 考虑virtual 函数以外的其他选择

因为其他的事情耽误了看书,现在将看的笔记记录下来. 1 class GameCharacter { 2 public: 3 virtual int healthValue() const; 4 }; 1. 藉由Non-Virtual Interface 手法实现 Template Method模式 (1)non-virtual interface(NVI): 令客户通过public non-virtual 成员函数间接调用private virtual函数 (2) virtual 函数的外覆器(

Effective C++笔记_条款25考虑写出一个不抛出异常的swap函数

1 // lib中的swap 2 namespace std { 3 template<typename T> 4 void swap (T& a, T& b) 5 { 6 T temp(a); 7 a = b; 8 b = temp; 9 } 10 } 11 12 // 缺点:需要赋值大量的数据,但是有的时候并不要复制如此多的内容 13 class WidgetImpl { 14 public: 15 //... 16 private: 17 int a, b, c; 18

Effective C++_笔记_条款08_别让异常逃离析构函数

(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) C++并不禁止析构函数吐出异常,但它不鼓励你这样做.考虑如下代码: 1: class Widget{ 2: public: 3: ... 4: ~Widget() {...} //假设这个可能吐出一个异常 5: }; 6:  7: void doSomething() 8: { 9: vector<Widget> v ; //v在这里被自动销毁 10: ...