条款41:了解隐式接口和编译期多态

  • class支持显示接口和运行期多态

    • class的显示接口由函数的名签式构成(函数名称、参数类型、返回类型)
    • class的多态通过virtual函数发生在运行期
  • template支持隐式接口和编译期多态
    • template的接口是隐式的,由具体的表达式决定
    • template的多态是通过其具现化和函数重载解析发生在编译期
//这里接口要求T必须实现operator >这个隐式接口
template<typename T>
T max(T a, T b){
return (a > b) ? a : b;
}

条款42:了解typename的双重意义

  • 声明template参数时,前缀关键字class和typename可以互换
  • 使用typename修饰嵌套从属类型名称
    • 不得在base class list(基类列)或member initialization list(成员初始化)内以typename作为base class的修饰符
#include <iostream>
#include <string>
#include <memory> using namespace std; template<typename T>
class Base {
public:
class Nested {
Nested(int _x) {}
private:
T val;
};
}; template<typename T>
class Derived : public Base<T>::Nested { //base class list (基类列)不加typename
explicit Derived(int x) : Base<T>::Nested(x) { //member initialization list(成员初始化)不加typename
typename Base<T>::Nested temp; //嵌套从属类型需要加typename
} }; int main() {
}

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

Template C++中继承并不是像Object Oriented C++中的那样适用:由于C++考虑到base class templates可能被特化,且特化版本可能不提供和一般template相同的接口。因此它拒绝在derived class中查找templatized base classes(模板化基类)中函数的名字。

  • 解决方法如下

    • 在base class函数调用动作之前加上“this->"
    • 适用using声明式指明模板化基类函数
    • 显示修饰指明模板化基类函数
template <typename T>
class Base{
public:
void print(T a) {cout <<"Base "<< a <<endl;};
}; template<typename T>
class Drive : public Base<T>{
public:
void printf(T a){
print(a); //error 编译器不知道基类有print函数
//解决方案
//this->print();
//using Base<T>::print
//base<T>::print直接调用
}
};

条款44:将与参数无关的代码抽离templates

  • Templates会生成多个classes和多个函数,所以要尽量消除template的膨胀参数

    • 类型模板参数可以让具有完全相同二进制表述的具现类型共享实现代码
    • 非类型模板参数可以用函数参数或class成员变量替换掉
#include <iostream>

using namespace std;

template<typename T>
class Base {
protected:
void show(T a, size_t n) {
cout << "base: " << a << endl;
cout << "size_t: " << n << endl;
}
}; template<typename T, size_t n>
class Derived : private Base<T> {
public:
void show(T a) {
Base<T>::show(a, n);
}
}; int main() {
Derived<int, 5> d;
d.show(1);
}

条款45:运用成员函数模板接受所有兼容类型

  • 使用成员函数模版生成“可接受所有兼容类型”的函数
  • 即使有了“泛化拷贝构造函数”和“泛化的赋值操作符”,仍然需要声明正常的拷贝构造函数和赋值操作符
template<typename T>
class shared_ptr{
public:
//拷贝构造函数,接受所有能够从U*隐式转换到T*的参数
template<typename U>
shared_ptr(shared_ptr<U> const &rh):p(rh.get()){
...
}
//赋值操作符,接受所有能够从U*隐式转换到T*的参数
template<typename U>
shared_ptr& operator= (shared_ptr<U> const &rh):p(rh.get()){
...
} //声明正常的拷贝构造函数
shared_ptr(shared_ptr const &rh);
shared_ptr& operator= (shared_ptr const &rh);
private:
T *p;
}

条款46:需要类型转换时请为模板定义非成员函数

在一个class template内,template名字可以被用来“template和其参数”的简略表达方式

  • 条款24中指出non-member函数能够在所有参数身上实施隐式转换

    • 隐式转换会在调用函数时进行
  • 然而在Template C++中模版函数在运行时会先具现然后再调用函数
    • 所以模版函数的参数在未知类型的情况下不会进行隐式转换
  • 解决方法是将non-member函数写成class template内部的friend函数
    • 使其具现过程在定义类的对象时便完成,再调用函数便可完成隐式转换

条款47:请使用traits class表现类型信息

  • template特化(Template Specialization)

    • 定义当类型模板参数为指定类型,所要运行的程序
//下一函数模板的通用定义:
template<typename T>
struct my_is_void {
static const bool value = false;
}; //对 void 类型,有以下的特化版本:
template<>
struct my_is_void<void> {
static const bool value = true;
}; //测试代码如下:
my_is_void<bool> t1;
cout << t1.value << endl; // 输出0
my_is_void<void> t2;
cout << t2.value << endl; // 输出1
  • template偏特化(Patial Spcialization)

    • 指定类型模板参数中的一部分,或使模板兼容参数的变形
//原始模板
template<typename T>
struct my_is_pointer {
static const bool value = false;
}; //对模板参数T进行限制,要求其为一个指针的类型:
template<typename T>
struct my_is_pointer<T*> {
static const bool value = true;
}; //测试:
my_is_pointer<int> p1;
cout << p1.value << endl; // 输出 0,使用原始模板
my_is_pointer<int*> p2;
cout << p2.value << endl; // 输出 1,使偏特化模板,因为指定了 int * 类型的参数
  • class traits使得“类的相关信息”在编译期就可用
  • 利用重载技术在编译期执行 if...else 语句
//STL五种迭代器
struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { }; // STL容器中list,set,multiset,map 和 multimap
struct random_access_iterator_tag : public bidirectional_iterator_tag { }; // STL容器中vector,deque 和 srting //定义容器中的迭代器
template< ... >
class deque {
public:
class iterator {
public random_access_iterator_tag iterator_category;
...
};
...
}; template< ... >
class list {
public:
class iterator {
public bidirectional_iterator_tag iterator_category;
...
};
...
}; //定义iterator_traits响应iterator classs的嵌套式typedef
template<typename IterT>
class iterator_traits {
public:
typedef typename IterT::iterator_category iterator_category;
...
}; //指针特化
template<typename IterT>
class iterator_traits<IterT*> {
public:
typedef typename IterT::iterator_category iterator_category;
...
}; //函数重载
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, random_access_iterator_tag) {
iter += d;
} template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, bidirectional_iterator_tag) {
if (d>=0) { while (d--) ++iter; }
else { while (d++) --iter; }
} template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, input_iterator_tag) {
if (d<0) {
throw out_of_range("Negative distance");
}
while (d--) ++iter;
} //调用
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d, input_iterator_tag) {
doAdvance(iter, d, typename iterator_traits<IterT>::iterator_category());
}

条款48:认识template元编程

  • 本质上就是函数式编程

    • C++元编程可以将计算转移到编译期,执行速度迅速(编译时间边长)
//上楼梯,每次上一步或者两步,有多少种
int climb(int n){
if(n == 1)
return 1;
if(n == 2)
return 2;
return climb(n - 1) + climb(n - 2);
} //元编程,采用类模版
template<int N>
class Climb{
public:
const static int n = Climb<N-1>::n + Climb<N-2>::n;
}; template<>
class Climb<2>{
public:
const static int n = 2;
}; template<>
class Climb<1>{
public:
const static int n = 1;
};

《Effective C++》模板与泛型编程:条款32-条款40的更多相关文章

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

    C++ templates的最初发展动机很直接:让我们得以建立“类型安全”的容器如vector,list和map.然而当愈多人用上templates,他们发现templates有能力完成愈多可能的变化 ...

  2. Effective C++ ——模板和泛型编程

    条款41:了解隐式接口和编译器多态 以public继承的类,

  3. Effective C++阅读笔记 较详细 复杂条款带样例

    一.让自己习惯C++ 条款01:视C++为一个语言联邦 C++可视为: C:以C为基础. 面向对象的C++:添加面向对象特性. 模板C++:泛型编程概念,使用模板. STL:使用STL的容器.迭代器. ...

  4. 【effective c++】模板与泛型编程

    模板元编程:在c++编译器内执行并于编译完成时停止执行 1.了解隐式接口和编译期多态 面向对象编程总是以显式接口(由函数名称.参数类型和返回类型构成)和运行期多态(虚函数)解决问题 模板及泛型编程:对 ...

  5. EC读书笔记系列之15:条款32、33、34

    条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...

  6. 《Effective C++》继承与面对对象设计:条款32-条款40

    条款32:确定你的public继承塑模出is-a关系 public继承意味着is-a.适用于base class身上的每一个函数也一定适用于derived class. 条款33:避免遮掩继承而来的名 ...

  7. C++ 模板与泛型编程

    <C++ Primer 4th>读书笔记 所谓泛型编程就是以独立于任何特定类型的方式编写代码.泛型编程与面向对象编程一样,都依赖于某种形式的多态性. 面向对象编程中的多态性在运行时应用于存 ...

  8. C++ Primer 学习笔记_76_模板与泛型编程 --模板定义[续]

    模板与泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...

  9. C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

    模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...

  10. C++ Primer 学习笔记_77_模板与泛型编程 --实例化

    模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实 ...

随机推荐

  1. Java设计模式——适配器模式(Adapter)

    目的:把源类型适配为目标类型,以适应客户端(Client)的需求:此处我们把目标接口的调用方视为客户端 使用场景:需要对类型进行由源类型到目标类型转换的场景中 前置条件:已有客户端 //Client ...

  2. java开发环境配置——JDK

    虽然网上有很多类似的文章了,第一次搭的时候也是看的网上的文章,但为了做个记录,自己也写一下,记录一下. 首先是先安装JDK,JDK下载可以直接去官网下载,地址:http://www.oracle.co ...

  3. react 函数子组件(Function ad Child Component)

    今天学习了react中的函数子组件的概念,然后在工作中得到了实际应用,很开心,那么好记性不如烂笔头,开始喽~ 函数子组件(FaCC )与高阶组件做的事情很相似, 都是对原来的组件进行了加强,类似装饰者 ...

  4. 20190322-a标签、img标签、三列表、特殊字符实体、表格

    目录 1.a标签 a标签的属性 锚点 2.img标签 img标签的属性 图像热区 3.三列表 有序列表(Ordered List)     ol>li 无序列表(Unordered List)  ...

  5. Dynamics 365新引入了多选选项集类型字段

    本人微信和易信公众号:微软动态CRM专家罗勇 ,回复276或者20180630可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me ...

  6. selenium chrome在新标签页打开链接的方法

    目前chrome是我在实现webdriver时运行最稳定的浏览器,如何利用webdriver打开多个标签页和链接呢,到处查找得到的往往只是如何打开标签页.打开标签页很简单,chrome浏览器打开标签页 ...

  7. iOS----------拨打电话的3种方式

    iOS实现拨打电话的方式:   方法一.requestWithURL,此方法拨打前弹出提示 NSMutableString * string = [[NSMutableString alloc] in ...

  8. [Android framework学习] ViewGroup的addView函数分析

    博客首页:http://www.cnblogs.com/kezhuang/p/ Android中整个的View的组装是采用组合模式. ViewGroup就相当与树根,各种Layout就相当于枝干,各种 ...

  9. 设置chrome浏览器背景颜色

    经常看博客,页面背景都是白色的居多,看久了眼睛就不适合了,决定修改chrome浏览器背景颜色,保护下自己的眼睛, 下载chrome 插件Stylish并安装,安装成功后chrome右上角有它的图标,点 ...

  10. Angualr学习笔记

    0.安装即环境初始化 下载node至windows,点击安装,所有环境变量直接OK: linux下载tar后,解压,在/etc/profile的path路径下增加node执行路径: export PA ...