码迷,mamicode.com
首页 > 其他好文 > 详细

[GeekBand] STL与泛型编程(1)

时间:2016-08-14 13:09:32      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:

在C++语法的学习过程中,我们已经对模板有了基本的了解。泛型编程就是以模板为工具的、泛化的编程思想。本篇文章介绍了一些在之前的文章中没有涉及到的一些模板知识、泛型编程知识和几种容器。关于模板的一些重复知识在这里就不再进行赘述。

一、关于模板的知识点补充

?

1. 函数模板的参数推导与显式指定

通常情况下,我们一般采用参数的自动推导方式去使用函数模板。在自动推导时,为了确保推导的正确性,C++不允许任何形式的隐式类型转换。

这个限制就导致了如下的情况:

template <class T>

int max(const T &a,const T &b)

{

????return a>b?a:b;

}

如果按照自动推导的方式,想要对1和2.0(int型和double型)进行比较,编译器是做不到的。为了解决这一问题,有如下两种方案:

第一种方案是显式进行类型转换。即max(double(1),2.0 );

第二种方案是采用显式指定模板的实例化,可采用这种语法:max<double>(1,2.0);

2. 模板的调用与匹配

当涉及到重载和模板共存时,编译器采用如下的顺序进行调用:

非模板重载函数 > 模板 > 进行隐式类型转换后再调用非模板重载函数。

当我们想强制调用模板函数时,可以使用如下的语法:

????Function_name <> (a,b);

利用一对尖括号表示要求调用模板方法。

3. 模板头中的常量使用

考虑如下的模板头:

template<typename T = int, size_t r = 1024>

其中的第二模板参数为一个size_t类型(其本质为int类型)的常量,在模板类中的使用可以理解成是一个宏定义。

这种写法的好处在于,例如,你在这个模板类中定义了一个大小为r的缓冲区并对其进行操作。

如果这里r不是模板参数,则由于普通的数组大小定义只能是固定的,只能够申请动态数组。然而如果使用了数字作为模板参数,则相当于在实例化的时候,r对于这个模板就是一个常量,因此可以写出类似于int a[r]之类的语句。

另外,这里还涉及到了模板实参的默认值。注意,无论是类型名还是模板常量,均可以设置默认值。

?

二、泛型编程基础知识

?

1. Traits(特性)

????Traits通常以一种抽象类的形式存在,用于提取出不同类型变量的共同特点。然后将这种特点应用到运算之中。

技术分享????首先考虑如下求和函数:

template<typename T>

T Sigma(const T* const a,int n)

{

????int rlt = 0;

????for(i=0;i<n;i++)

????????rlt += a[i];

}

????表面上看,这个求和函数不存在什么问题,然而,当我们将这一求和函数应用到这样的场景时:

char a[10] = "zzzzzzzz";

cout << Sigma(a,10);

结果却不是‘z‘对应的ASCII的9倍,而小于这个值。其原因在于,char类型的变量只能表示0~255之间的数字,当结果大于255时,发生了数据溢出。显然,这不是我们在求和的过程中希望看到的结果。Char,short求和的返回类型至少应该是int,float返回类型应该是double,int返回类型应该是long……

对于这个函数来说,所谓的"特性"就是他们的数值属性,将这个属性提取出来,可以采用模板的特化的办法:

template <typename T> class Sigmatraits {};

template <> class Sigmatraits<char>{

????public: typedef int ReturnType

}

template <> class Sigmatraits<short>{

????public: typedef int ReturnType

}

//新的Sigma的定义

typename Sigmatraits<T>::ReturnType

Sigma(const T * const a,int n)

{

typedef typename Sigmatraits<T>::ReturnType ReturnType;

ReturnType S = ReturnType ();

for(int i=0;i<n;i++)

???? S+=a[i];

return S;

}

注意新的Sigma的定义中,typename Sigmatraits<T>::ReturnType 是返回值的类型

?

????这里需要解释一下上面的返回值类型中,typename关键字的作用。

????在作为模板参数时,typename和class关键字的作用是完全相同的,当typename不作为class的同义字使用时,考虑如下代码:

struct A{

????typedef int bar;

}

template<class T>

void foo(const T & t)

{

????T::bar *p; //这里是定义了一个p?还是T::bar作为一个变量乘以p

}

int main()

{

????A x;

????foo(x);

}

上述代码看似没有什么问题,但是实际上是无法编译通过的。这涉及到了函数模板的二次编译。第一次编译时,对于函数模板foo,编译器不知道T::bar是一个什么东西。究竟是一个变量名,还是一个类型名?对于这种情况,C++规定默认情况下,class::member 表示一个成员变量,因此对于foo函数,则产生了"使用了未定义的变量p"的错误。如果要求member表示一个类型名,则必须显式地声明其为typename类型,即:

void foo(const T & t)

{

????typename T::bar *p; //通过编译

}

2. 迭代器——指针的泛化

????在之前C++的学习中,已经对迭代器进行了一个初步的介绍,迭代器的使用方法与指针类似。

  • 迭代器是容器和算法的接口,算法通常要以迭代器作为参数。
  • 迭代器使算法和容器分离开,使算法不再依赖于数据结构(容器)。
  • 迭代器又使算法和容器粘合在一起,使算法能够应用于不同的容器。

对于一般的STL迭代器,有以下的方法:

*iter

?存取实际元素

iter->member

?读取实际元素的成员

++iter

?向前步进,传回新位置

iter++

?向前步进,传回旧位置

--iter

?退步(传回新位置)

iter--

?退步(传回旧位置)

iter1== iter2

?判断两个迭代器是否相等

TYPE(iter)

?复制迭代器(copy构造函数)

TYPE()

?产生迭代器(default构造函数)

iter1=iter2

?赋值

另外,对于可以随机存取的数据结构,其还具有如下随机存取操作:

iter[n] ?

取索引位置为n的元素

iter += n

?向前跳n个元素

iter -= n ?

向后跳n个元素

iter + n ?

传回iter之后的第n个元素

n + iter ?

传回iter之后的第n个元素

iter - n ?

传回iter之前的第n个元素

iter1 - iter2 ?

传回iter1iter2之间的距离

iter1 < iter2 ?

判断iter1是否在iter2之前

iter1 > iter2 ?

判断iter1是否在iter2之后

iter1 <= iter2

判断iter1是否不在iter2之后

iter1 >= iter2

判断iter1是否不在iter2之前

[GeekBand] STL与泛型编程(1)

标签:

原文地址:http://www.cnblogs.com/shawnChi/p/5769964.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!