您的位置:首页 > 编程语言 > C语言/C++

《Effective C++》学习笔记——条款43

2016-10-13 16:00 330 查看

七、模板与泛型编程

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

开始,先写一个程序,它能够传送信息到若干不同的公司去。信息要么就是被译成密码,要么就是未经过加工的文字。

如果在编译期内,我们拥有足够的信息来决定哪一个信息传递到哪一家公司,就可以采用template的方法。

class CompanyA  {
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
};
class CompanyB  {
public:
...
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
...
};
...                                     // 针对其他公司设计的类
class MsgInfo  {     ...     };         // 保存信息,以备将来产生信息
template<typename Company>
class MsgSender  {
public:
...                                // 构造、析构函数等
void sendClear(const MsgInfo& info)
{
std::string msg;
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info)
{
std::string msg;
Company c;
c.sendEncrypted(msg);
}
};


这个做法是可以的,而且如果我们需要在送出信息前打印某些信息也可以轻松在派生类中加上。

template<typename Company>
class LoggingMsgSender: public MsgSender<Company>  {
public:
...
void sendClearMsg(const MsgInfo& msg)
{
// 此处打印发送前的信息
sendClear(info);
// 此处打印发送后的信息
}
...
};


但是在上面代码中,
sendClear
调用了基类的函数,会无法通过编译,编译器提示你
sendClear
不存在。

Why?明明在基类里!

因为,当编译器遇到
class template LoggingMsgSender
定义式的时候,并不知道它继承了什么样的类。

显然它继承了
MsgSender<Company>
,但是这里面的
Company
是一个tamplate参数,不到后来
LoggingMsgSender
被具现化,并不能确切的知道它是什么,因此就无法知道它是否有个
sendClear
函数。

更深的来说,是因为C++知道
base class templates
有可能被特化,而特化版本可能不提供一般性template相同的接口。

关于特化

template<>
class MsgSender<CompanyZ>  {
public:
...
void sendSecret(const MsgInfo& info)
{     ...     }
...
};


类前的 template<> 语法 表示这既不是template也不是标准class,而是一个特化版本的MsgSender template,只有在template实参是CompanyZ时调用。

这就是模板全特化,通过特化,可以不提供一般性template相同的接口。

因此,当CompanyZ在上面调用 sendClear时,就没有这个函数。

话题转过来,如何解决呢?

我们需要让C++ 进入templatized base class观察。

这里有三个办法:

在基类函数调用动作之前加上
this->


sendClear(info);
改成
this->sendClear(info);


使用
using声明式


告诉编译器,假设
sendClear
位于基类内

LoggingMsgSender
类内添加
using MsgSender<Company>::senderClear;


指出被调用的函数位于基类内

sendClear(info);
改成
MsgSender<Company>::sendClear(info);


但这个办法不是很好,因为当被调用的是virtual函数时,明确的资格修饰会关闭virtual绑定行为。

其实,上面所有的做法都是在保证:base class template的任何特化版本都将支持一般(泛化)版本所提供的接口。

请记住

可在
derived class template
内通过 “
this->
“指涉
base class templates
内的成员名称,或通过直接写出的 “
base class 资格修饰符
” 完成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息