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

来源:互联网 发布:js foreach 跳出循环 编辑:程序博客网 时间:2024/05/10 10:23

(一)

注意从 “面向对象的C++” 转向 “模板C++” 时继承可能遭遇问题 :由于基类模板可能被特化,而该特化版本可能会改变成员,因此C++拒绝在模板化基类中寻找继承而来的名称。


(二)

看下面的例子:

假设将信息传送到不同的公司去,传送方式包括明文传送和密文传送,采用模板类的设计方法:

class CompanyA {public:...void sendClearText(const string& msg);void sendEncrypted(const string& msg);...};class CompanyB {public:...void sendClearText(const string& msg);void sendEncrypted(const string& msg);};...//其他公司的classesclass MsgInfo {...}; //这个class为将来生产保存信息template<typename Company>class MsgSender {public:...void sendClear(const MsgInfo& info) {string msg;...//根据info产生信息Company c;c.sendClearText(msg);}void sendSecret(const MsgInfo& info) {...}//这里调用的是c.sendEncrypted};
如果想要添加发送信息时记录信息的功能,采用继承的方式:

template<typename Company>class LoggingMsgSender : public MsgSender<company> {public:...void sendClearMsg(const MsgInfo& info) {...//将传送前的信息写至记录信息sendClear(info);//试图调用基类成员函数完成发送,但是不能通过编译。这是因为在派生类没有具体化时,不知道Company是什么类型,也就不知道基类中是否存在该函数...//将传送后的信息写至记录信息}...};
        编译器不让其通过编译.此时的编译器抛出了"sendClear不存在"的抱怨.可sendClear明显就在base class内部啊?郁闷,这是为什么倪?别急,我来解释一下:当编译器遇到LoggingMsgSender定义式时,由于Company是一个template参数,不到LoggingMsgSender被具现化的时候,编译器是无法知道Company是什么的.由于无法知道Company是什么,那么它就无法确定MsgSender是什么,也就无法获知其否有个sendClear函数. 停一下,你可能现在对我的解释会有一个问题:为什么根据template Company就不能确定Company是什么了呢?template Company不是有声明式嘛!在这里我提出:我们不能根据一般性模板类的声明式来确定具现化类具有的操作,因为模板类有个特化声明版本的问题.

(三)

改进方法:

(1)在基类函数调用动作之前加上this指针:

template <typename Company>     void LoggingMsgSender<Company>::sendClearMsg(const MsgInfo& info){         ...         this->sendClear(info); //ok         ...     }

(2)使用using声明式在类内使用基类命名空间:

template <typename Company>     class LoggingMsgSender:public MsgSender<Company>{     public:         //这里的情况不是base class名称被derived class名称遮掩,而是编译器不进入base base         //作用域查找,于是我们通过using声明式告诉它,请它这么做         using MsgSender<Company>::sendClear;//告诉编译器,请它假设sendClear位于base class内         ...         void sendClearMsg(const MsgInfo& info){             ...             sendClear(info);//ok             ...         }     };

(3)明确限定函数是基类中的函数:

 template <typename Company>     class LoggingMsgSender:public MsgSender<Company>{     public:         ...         void sendClearMsg(const MsgInfo& info){             ...             MsgSender<Company>::sendClear(info); //ok             ...         }         ...     };
不过此方法有一个很明显的暇疵:如果被调用的是virtual函数,上述的明确资格修饰会关闭"virtual绑定行为".
所以还是尽量选择前两种方法吧。


请记住:

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







0 0
原创粉丝点击