《C++ Templates》读书笔记

第二章  函数模版

模板函数代表一个函数家族,看起来跟普通函数很相似,唯一区别就是模板函数中某些元素是未确定的,在使用时候这些元素被参数化。

返回两个值中最大者的模板函数:

template<typename T>
inline T const& max (T const& a, T const& b)
{
  return a < b ? b : a;
}
这个模板定义了一个“返回两者中最大者”的函数家族,输入两个参数,参数类型未确定,指定模板参数T来替代。
这个例子中,T需要支持operator<,因为两个参数是使用这个操作符进行比较的。
typename可以用class来替代,在这里两者等价,这是历史原因造成的,因为typename出现比较晚,先前被使用的class被保留了下来。
调用示例:

int main()
{
  string s1 = "abcd", s2 = "ABCD";
  string rs = ::max<string>(s1, s2);

  int n1 = 10, n2 = 11;
  int rn = ::max<int>(n1, n2);

  double f1 = 7.8, f2 = 7.5;
  double rf = ::max<double>(f1, f2);

  return 0;
}
上面max被调用了3次,3次调用参数类型不同,实际上,max被实例化了3次,分别实例化成参数类型为string,int,double的函数。
只要使用函数模板,编译器会自动实例化, 实例化发生在编译期间。所以如果参数使用不当,编译时就会报错。

第三章 类模版

1. 类模板的定义

类模板的声明格式:

template <typename T>
class Stack{
  ...
}
在类模板内部,T可以像其他任何类型一样使用,看下面的例子:

template <typename T>
class Stack{
private:
  std::vector<T> elemes;    // 存储元素的容器

public:
  Stack();                  // 构造函数
  void  Push(const T& e);   // 压入元素
  void  Pop();              // 弹出元素
  T     Top() const;        // 返回栈顶元素
};
这个类的类型是Stack<T>,其中T是模板参数,因此当在声明中需要使用类的类型时,必须使用Stack<T>,例如要增加拷贝构造函数和赋值函数,就要这样写:

template <typename T>
class Stack{
  ...
  Stack(const Stack<T> &);
  Stack<T>& operator=(const Stack<T> &);
}
然而,当使用类名而不是类的类型时,就应该只用Stack,譬如,当指定类名、构造函数、析构函数时就用Stack.
成员函数的实现:
1. 必须指定是一个模版函数,即加template <typename T>
2. 要使用类的完整限定符,即Stack<T>::

template <typename T>
void Stack<T>::Push(const T& e)
{
  elemes.push_back(e);
}
2. 类模板的使用
Stack<int> intStack;
Stack<std::string> strStack;

intStack.Push(7);
strStack.Push("hello");
对于类模板,成员函数只有被调用的时候才被实例化,另外,如果类中含有静态成员,那么用来实例化的每种类型,都会实例化静态成员。

3. 类模板的特化

类模板的特化跟函数重载类似,通过特化类模板,可以优化基于某种特定类型的实现。

要特化类模板,就要特化类模板的所有成员函数。
为了特化一个类模板,要在起始处声明一个template<>.接下来声明用来特化模版的类型,这个类型被用作模版实参。

template<>
class Stack<std::string>{
  ...
}
进行类模板的特化时,每个成员都必须重新定义成普通函数,原来模版函数中每个T也相应地被特化的类型取代。

void Stack<std::string>::Push(const std::string& e)
{
  elems.push_back(e);
}
4. 局部特化
template<typename T1, typename T2>
class MyClass{
  ...
};
上面的类模板有一下几种特化类型:

// 局部特化,两个模板参数类型相同
template<typename T>
class MyClass<T, T>{
  ...
};

// 局部特化,第2个模板参数是int
template<typename T>
class MyClass<T, int>{
  ...
};

// 局部特化,两个模板参数都是指针类型
template<typename T1, typename T2>
class MyClass<T1*, T2*>{
  ...
};
下面例子展示各种声明会使用哪个模板

MyClass<int, float> mif;        // 使用 MyClass<T1, T2>
MyClass<float, float> mff;      // 使用 MyClass<T, T>
MyClass<float, int> mif;        // 使用 MyClass<T, int>
MyClass<int*, float*> mif;      // 使用MyClass<T1*, T2*>

5.  缺省模版实参

可以为类模板的参数定义缺省值,这些值还可以引用之前的模板参数。
类模板参数有缺省值的例子:

// 类模板参数有缺省值的例子:
template <typename T, typename CONT = std::vector<T>>
class Stack{
private:
  CONT elems;

public:
  void    Push(const T&);
  void    Pop();
  T       Top() const;
};

template <typename T, typename CONT>
void Stack<T, CONT>::Push(const T& e)
{
  elems.push_back(e);
}

// 其它实现省略 ...
使用示例:

Stack<int> intStack;  // int栈
// 存储double元素,使用deque管理double元素
Stack<double, std::deque<double>> dblStack;
第五章 技巧性基础知识
1. typename关键字

当依赖于模板参数的名称是类型时,就应该使用typename.

template<T>
class MyClass
{
  typename T::SubType * ptr;  // typename用来说明SubType是定义于T内的一种类型,如果不使用typename,SubType会被认为是T的一个静态成员
  ...
};
2. 使用this->

在类模板中,在基类中声明的函数或变量,在调用时应该使用this->或Base<T>::限定访问,否则容易出错。

3. 模板类型的变量的初始化

template<typename T>
void foo()
{
  T x = T();    // 如果T是内建类型,x是0或false.
}

template<typename T>
class MyClass{
private:
  T x;
public:
  MyClass() : x() { // 通过初始化类表来初始化类模板成员

  }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值