Effective C++(七)模板与泛型编程

七、模板与泛型编程
条款41:了解隐式接口和编译器多态

1、面向对象
1)显式接口
类型必须支持class的接口,在源码中明确可见。基于函数签名式
2)运行期多态
对于某些virtual成员函数,运行时根据动态类型决定调用哪个函数

2、template及泛型编程
1)隐式接口
类型支持的接口,有template中的需要决定。基于有效表达式
2)编译器多态
以不同的template参数具现化function template会导致调用不同的函数

3、请记住
1)class和template都支持接口和多态
2)对class而言接口是显式的,以函数签名为中心,多态则是通过virtual函数发生于运行期
3)对template参数而言,接口是隐式的,基于有效表达式,多态则是通过template具现化和函数重载解析发生于编译期

条款42:了解typename的双重意义
1、class和typename
template class Widget;
template class Widget;
两个声明式意义完全相同

2、必须使用typename的情形
从属名称:template内出现的名称相依与某个template参数
嵌套从属名称:从属名称在class内呈嵌套状
C++解析规则:在template中遭遇嵌套从属名称,便假设不是类型,除非告诉它!
template
void print2nd(const C& container)
{
   if(container.size()>=2){
      typename C::const_iterator iter(container.begin());  //正确!
   }
}
注:只被用来验证嵌套从属类型名称,其它名称不该有!

3、规则例外
1)不可出现在base class list内的嵌套从属类型名称之前
2)不可在member initialization list(成员初值列)中作为base class修饰符

4、请记住
1)声明template参数时,class和typename可互换
2)请使用typename标识嵌套从属类型名称,但不得在base class list(基类列)或member initialization list(成员初始列)内以它作为base class修饰符

条款43:学习处理模板化基类内的名称
1、template继承
继承的base class,包含template参数,不到被具现化无法确切知道它是什么

2、模板全特化
例子:
template<>     //既不是template也不是标准class。而是一个特化版的template
class MsgSender{       //在template实参是CompanyZ使用
public:
   void sendSecret(const MsgInfo& info)
};
当base class被指定为MsgSender时,未提供sendClear函数,编译被拒绝!
原因:base class有可能被特化,而特化版本可能不提供和一般性template相同的接口,拒绝在模板化基类中寻找继承来的名称
解决方法:
1)在base class函数调用动作之前加上"this->"
this->sendClear(info);
2)使用using声明式
using MsgSender::sendClear;
编译器进入base class作用域查找
3)明确指出被调用函数位于base class
MsgSender::sendClear(info);
问题:如果被调用的是virtual函数,明确资格修饰会关闭"virtual绑定行为"

3、请记住
可在derived class template内通过"this->"指涉base class template内的成员名称,或写出"base class资格修饰符"

条款44:将与参数无关的代码抽离template
1、template重复
重复是隐晦的,需要注意template被具现化多次时可能发生的重复

2、请记住
1)template生成多个class和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系
2)因非类型模板参数造成的代码膨胀,可以消除,做法是以函数参数或class成员变量替换template参数
3)因类型参数造成的代码膨胀,可降低,做法是让带有完全相同二进制表述的具现类型共享实现码

条款45:运用成员函数模板接受所有兼容类型
1、智能指针
行为像指针的对象,可以在正确的时间自动删除heap-base资源
真实指针支持隐式转换,derived class指针可以隐式转换为base class指针,"指向non-const对象"的指针可以转换为"指向const对象"

2、同一template具现体
同一template的不同具现体之间并不存在什么与生俱来的固有关系
例子:如果带有base-derived关系的B,D两类型分别具现化某个template,产生出来的两个具现体并不带有base-derived关系

3、template和泛型编程
1)member function template
对任何类型T和任何类型U,可以根据SmartPtr生成SmartPtr
泛化copy构造函数:构造函数根据对象U创建对象T,U和T的类型是同一个template的不同具现体
template
class SmartPtr{
public:
   template
   SmartPtr(const SmartPtr& other)
   :heldPtr(other.get()){ ... }
   T* get() const { return heldPtr; }
private:
   T* heldPtr;
};
使用成员初值列初始化类型为T*的成员变量,并以类型为U*的指针作为初值
只有存在某个隐式转换可将一个U*指针转为一个T*指针时才能通过编译!

4、请记住
1)请使用member function template生成"可接受所有兼容类型"的函数
2)如果你声明member template用于"泛化copy构造"或"泛化assignment操作",还需要声明正常的copy构造函数和copy assignment操作符

条款46:需要类型转换时请为模板定义非成员函数
1、template实参推导
template实参推导过程并不将隐式类型转换函数考虑!不会使用"通过构造函数而发生的"隐式类型转换

2、friend函数
template
class Rational{
public:
   friend const Rational operator*(const Rational& lhs, const Rational& rhs)
   {
      return Rational(lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator());
   }
};
template
const Rational operator*(const Rational& lhs, const Rational& rhs);
当oneHalf被声明为Rational,class Rational被具现化出来,friend函数operator*(接受Rational)也就被自动声明出来

3、请记住
编写class template,提供于此template相关的函数支持所有参数的饮食类型转换时,定义函数为class template内部的friend函数

条款47:请使用traits classes表现类型信息
1、STL迭代器分类
1)input迭代器
只能向前移动,一次一步,只能读不能写,只能读一次。
例:istream_iterator
2)output迭代器
只能向前移动,一次一步,只能写,而且只有一次。
例:ostream_iterator
3)forward迭代器
基于前两者,而且读写次数一次以上
例:slist,TR1 hash
4)Bidirectional迭代器
基于前三,而且可以向前向后
5)random迭代器
基于前四,可以执行"迭代器算术"

2、请记住
1)traits classes使得"类型相关信息"在编译器可用,它们以template和templates特化完成实现
2)整合重载技术后,traits classes有可能在编译期对类型执行if...else测试

条款48:认识template元编程
1、请记住
1)template metaprogramming(TMP,模板元编程)可将工作由运行期移往编译期,实现早期错误侦测和更高的执行效率
2)TMP可被用来生成"基于政策选择组合"的客户定制代码,也可避免生成对某些特殊类型并不适合的代码

你可能感兴趣的:(C++提高)