c++Templates学习笔记(一)

第一部分:基础

第二章:函数模版

(1)编程规范:应将修饰符“*”和“&”紧靠数据类型,int*  p;或者int&  p。 int const x和const int x是等价的,但是比较规范的写法是 int const x,这样写的原因是:如果有

typedef char* CHARS;  
typedef CHARS const CPTR; 
那么我们使用CPTR的时候,它其实是一个指针常量。如果我们直接将CHARS所代表的内容替换到下面,将会展开成下面的情况

typedef char* const CPTR;
这样的简单替换就能很明了的明白替换后的意思。如果写成

typedef char* CHARS;  
typedef const CHARS CPTR;
那么替换后将变成

typedef const char* CPTR;
将导致误解,所以比较规范的情况就是讲const写在类型之后。

int const *p 等价于 const int *p 是指常量指针,int * const p 是指针常量。

static修饰的全局变量,将会隐藏该变量,该变量在其他文件中是不可见的,如果不加static那么在其他文件中是可见的。

const默认是局部的,要想在其他文件中见到,需要加上extern

(2)::限定符表示的是全局的。用具体类型代替模版参数的过程叫做实例化。模版被编译两次,第一次:实例化之前,检查模版代码本身,看是否有语法错误。第二次:在实例化期间,检查模版代码,查看是否所有的调用都有效。

(3)在函数模版内部不能指定缺省的模版参数,在类模版中能够指定缺省的模版参数。模版的演绎分为两种,一种就是显式的指定,一种就是演绎推导。显式的指定也可以不全部指定,只指定模版参数的前几个,剩下的要演绎推导。

(4)函数模版也可以进行重载,一个非模版函数可以和一个同名的函数模版同时存在,调用的优先级非模板函数的优先级高。<>这个符号是显式的指定使用模版函数,并且模版参数为空,要用演绎推导进行参数匹配。

第三章:类模板stack的实现

(1)看下面的例子

#include<iostream>
#include<vector>
#include<string>

using namespace std;

template<typename T>
class Stack
{
    private:
   vector<T> managerV;
    public:
    void push( T const& ) ;
    void pop( ) ;
    T const top() const ;
    bool empty() const;
};

template<typename T>
void Stack<T>::push( T const& element  )
{
    managerV.push_back( element );
}

template<typename T>
void Stack<T>::pop()
{
    if( managerV.empty() )
        throw out_of_range("Stack<>::pop():empty stack");
    managerV.pop_back();
}
template<typename T>
T const Stack<T>::top() const
{
    if( managerV.empty() )
        throw out_of_range("Stack<>::top:empty stack");
    return managerV.back();
}
template<typename T>
bool Stack<T>::empty() const
{
    return managerV.empty();
}

void main()
{
    try
    {
        Stack<int> intStack;
        Stack<string> stringStack;

        intStack.push(7);
        cout << intStack.top() << endl;

        stringStack.push("hello");
        cout << stringStack.top() << endl;
        stringStack.pop();
        stringStack.pop();
    }
    catch( exception const& ex )
    {
        cerr << "Exception:" << ex.what() << endl;
    }
}
对于类模版成员函数只有在被使用的时候才会产生某些函数的实例化代码,这样做的第一个好处是可以节省空间和时间,另外一个好处是对于那些没有提供某些成员函数的类型,你也可以使用这些类型,只要不使用没有提供的成员函数就不会出现错误。在上例中对于int版本只实例化了push()和top(),而对于string版本则实例化了所有的成员函数。如果类模版中含有静态成员,那么用来实例化的每种类型,都会实例化这些静态成员。

(2)你可以用模版实参特化类模版,和函数模版的重载类似,通过特化类模版,你可以优化基于某种特定类型的实现,或者克服某种特定类型在实例化模版时所出现的不足。另外,如果要特化一个类模版,你还要特化类模版的所有成员函数,虽然也可以只特化某个成员函数,但这种做法并没有特化整个类,也没有特化整个类模版。为了特化一个类模版,你必须在起始处声明一个template<>,接下来声明用来特化类模版的类型

template<>
class Stack<string>
{};
(3)类模版可以被局部特化,你可以在特定环境下指定类模板的特定实现,并且要求某些模版参数仍然必须由用户来定义,例如:

template<typename T1 ,typename T2>
class MyClass{};
可以被局部特化成下面几种形式:

template<typename T>
class MyClass<T,T>{};//如果T1和T2是同一种类型,将使用这种局部特化

template<typename T>
//如果T1是一种类型T2是int类型,将使用这种局部特化
class MyClass<T,int>{};

template<typename T1,typename T2>
//如果T1和T2是两种类型的指针,将使用这种局部特化
 class MyClass<T1*,T2*>{};

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

MyClass<int,int> mif; //错误:同等程度的匹配MyClass<T,T>和MyClass<T,int>
//解决办法是再重新定义一个
template<>
class MyClass<int ,int>{};这样的一个类
(3)缺省模版参数

对于类模版,你还可以为模版参数定义缺省值,这些值被称为缺省模版实参,而且他们还可以引用之前的模版参数

#include<iostream>
#include<vector>
#include<string>

using namespace std;

template<typename T,typename CONT = vector<T> > //使用了第一个参数T,默认采用vector实现
class Stack
{
    private:
    CONT managerV;
    public:
    void push( T const& ) ;
    void pop( ) ;
    T const top() const ;
    bool empty() const;
};

template<typename T,typename CONT>
void Stack<T,CONT>::push( T const& element  )
{
    managerV.push_back( element );
}

template<typename T,typename CONT>
void Stack<T,CONT>::pop()
{
    if( managerV.empty() )
        throw out_of_range("Stack<>::pop():empty stack");
    managerV.pop_back();
}
template<typename T,typename CONT>
T const Stack<T,CONT>::top() const
{
    if( managerV.empty() )
        throw out_of_range("Stack<>::top:empty stack");
    return managerV.back();
}
template<typename T,typename CONT >
bool Stack<T,CONT>::empty() const
{
    return managerV.empty();
}

void main()
{
    try
    {
        Stack<int> intStack;
        Stack<double ,deque<double> > dblStack; //使用deque实现

        intStack.push(7);
        cout << intStack.top() << endl;

        stringStack.push(42.42);
        cout << dblStack.top() << endl;
        dblStack.pop();
        dblStack.pop();
    }
    catch( exception const& ex )
    {
        cerr << "Exception:" << ex.what() << endl;
    }
}
第四章:非类型模版参数

(1)非类型模版参数的用法

template<typename T ,int MAXSIZE>
class Stack
{
  private:
   T elems[MAXSIZE];
};

每个模版实例都具有自己的类型,因此int20Stack和int40Stack是两种不同的类型,而且这两种类型不能显式或者隐式的转化,他们之间不能相互替代更不能相互赋值。

非模板参数,只能是长整数或者指向外部链接对象的指针,不能是浮点数,class类型的对象和内部链接对象,如:

template <double VAT>
double process(double v) //错误,浮点数
{
   return v * VAT;
}

template <string name > //错误,类类型
class MyClass{};

template<char const* name >
class MyClass{};
MyClass<"hello">  x; //错误,“hello”是字符串文字,是内部链接对象

template<char const* name >
class MyClass{};
extern char const s[] = "hello";
 MyClass<s>  x; //正确,s是外部链接对象




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值