- Rule41: 了解隐式接口和编译期多态
understand implicit interfaces and compile-time polymorphism.
面向对象编程总是以显式接口(explicit interfaces)和运行期多态(runtime polymorphism)解决问题。
请记住:
(1)classes和Template都支持接口和多态
(2)对class而言接口是显式的,以函数签名为中心,如下面直接的函数签名为定义:
Class Widget
{
public:
Wigdet();
virtual~ Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
}。
多态则是通过virtual函数发生于运行期。
(3)对Template参数而言,接口是隐式的,奠基与有效表达式。多态则是通过Template具现化和函数重载解析发生于编译期。
- Rule42: 了解typename的双重意义
understand the two meanings of typename.
一种常用的做法是在定义模板类或模板方法中使用。
还有一种情况如下描述:
template<typename C>
void print2nd(const C& container)
{
if(container.size >=2)
{
C::const_iterator iter(container.begin());//编译通不过
++iter;
int value = *iter;
std::out<<value;
}
}
这种写法编译不能通过,因为编译器默认情况下不会将“嵌套从属名称”视为一种类型,除非用户自己指定。。。
指定方法也比较固定:
typename C::const_iterator iter(container.begin());
- Rule43:学习处理模板化基类内的名称
Know how to access names in templatized base classes.
先看如下例子,CompanyA和CompanyB提供了两种相同的方法,根据不同公司处理的算法不同,MsgSender是一个消息发送类,可以发送公司的消息,明文形式或者加密形式。
#include <iostream>
using namespace std;
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;
//这里,根据info产生信息
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info)
{
std::string msg;
//这里,根据info产生信息
Company c;
c.sendEncrypted(msg);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
MsgSender<CompanyA> ca;
ca.sendClear(MsgInfo());
getchar();
return 0;
}
以上代码能正常进行编译运行。
现在假设需要在发送信息的时候增加日志记录的附加功能,我们继承MsgSender,如下所示。
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
void sendClearMsg(const MsgInfo& info)
{
//将“传送前”的信息写至log
//调用base函数
sendClear(info);
//将“传送后”的信息写至log;
}
void sendSecretMsg(const MsgInfo& info)
{
//将“传送前”的信息写至log
//调用base函数
sendSecret(info);
//将“传送后”的信息写至log;
}
};
Effective C++指出上述代码在某些严格编译器上会编译出错,提示找不到标志符 “sendClear”和“sendSecret”。 理应是可以的,因为它就是继承自MsgSender,这两个方法本身就存在。本人在VS 2010 IDE中编写了上述代码,能够正常运行。
保险起见,作者给出了三种解决方法:
第一种是在base class函数调用动作之前加上 this->。如下形式:
this->sendClear(info);
第二种是使用using声明式,
如下: using Msgsender<Company>::sendClear
//明确告诉编译器,请它假设sendClear位于base class内。
这种做法是在有的编译器不进入base class作用域内查找,于是我们通过using告诉它,请它那么做。
第三种做法是,明白指出被调用的函数位于base class内。
Msgsender<Company>::sendClear(info)
//OK,假设sendClear将被继承下来
但这种方法往往是不让人满意的,因为如果被调用的是virtual函数,上述的声明方法会关闭“virtual绑定行为”