模板

1.模板定义


模板是泛型编程的基础,所谓泛型编程即编写与类型无关的逻辑代码,是一种复用方式。
分为:
(1)模板函数
(2)模板类

2.模板函数


格式:

template <class/typename 形参名1,class/typename 形参名2>
返回类型 函数名(参数列表){...}

基于普通函数讲解模板函数的实用性:

//普通函数
bool Equal(int x1, int x2)
{
    return x1 == x2;
}
bool Equal(const string& s1, const string& s2)
{
    return s1 == s2;
}

void TestEqual()
{
    string s1="abc";
    string s2="cde";
    cout << Equal(s1, s2) << endl;
    cout << Equal(2,2)<< endl;

}

若要比较char/double型还需实现多个函数,过于麻烦吧!接下来体会模板的复用之处。

//模板实现函数
template <class T>

bool Equal(const T& s1, const T& s2)
{
    return s1 == s2;
}

void TestEqual()
{
    string s1="abc";
    string s2="cde";
    cout << Equal(s1, s2) << endl;
    cout << Equal(2,2)<< endl;

}

这样仅使用一份代码,即可实现编写者的需求,复用性高
编译器推演过程如下:
这里写图片描述

汇编层查看如下:
这里写图片描述

3.模板参数匹配及显示实例化


借助代码:

template <class T>
bool Equal(const T& s1, const T& s2)
{
    return s1 == s2;
}
void TestEqual()
{
    //cout << Equal(1, 1.3) << endl;//出错,编译不通过
    cout << Equal<int>(1,2) << endl;//显示实例化
    cout << Equal<double>(2.5,2)<< endl;
}

这里写图片描述

4.模板类


这里借助用模板实现Vector解释

模板类的格式:
template< class 形参名1 , class 形参名2 , … c lass 形参名n>
class 类名
{ … };

template <class T>

class Vector
{
public:
    //构造函数
    Vector()
        :_first(NULL)
        , _finish(NULL)
        , _endofstorage(NULL)
    {}

    //拷贝构造函数
    Vector(const T& v)
    {
        if (v.Size() > 0)//不为空
        {
            _first = new T[v.Size()];
            memcpy(_first, v._first, sizeof(T)*v.Size());

            _finish = _first + v.Size();
            _endofstorage = _first + v.Size();
        }
        else//为空
        {
            _first = _finish = _endofstorage = NULL;
        }
    }

    //析构函数
    ~Vector()
    {
        delete[] _first;
        _first = _finish = _endofstorage;
    }

    size_t Capacity() const
    {
        return _endofstorage - _first;

    }

    //赋值运算符重载v1=v2
    Vector<T>& operator=(T& v)
    {
        swap(_first, v._first);
        swap(_finish, v._finish);
        swap(_endofstorage, v._endofstorage);
        return *this;
    }

    size_t Size()const
    {
        return _finish - _first;
    }

    //扩容
    void Expand(size_t n)
    {
        if (n > Capacity())
        {
            size_t size = Size();
            T* tmp = new T[n];
            if (_first)
            {
                memcpy(tmp, _first, sizeof(T)*Size());
                delete[] _first;
            }
            _first = tmp;
            _finish = _first + size;
            _endofstorage = _first + n;
        }

    }

    void PushBack(T x)
    {
        //判满
        if (_finish == _endofstorage)
        {
            if (Capacity() == 0)
            {
                Expand(3);
            }
            else
            {
                Expand(Capacity() * 2);

            }
        }
        *_finish = x;
        _finish++;
    }
    void PopBack()
    {
        assert(_finish > _first);
        --_finish;
    }

    void Insert(size_t pos, T x)
    {
        assert(pos <Size());
        if (_finish == _endofstorage)
        {
            if (Capacity() == 0)
            {
                Expand(3);
            }
            else
            {
                Expand(Capacity() * 2);
            }
        }
        int end = Size() - 1;//下标
        while (end >= (int)pos)
        {
            //后移数据
            _first[end + 1] = _first[end];
            --end;
        }
        _first[pos] = x;
        ++_finish;
    }

    void Erase(size_t pos)
    {
        assert(pos < Size());
        size_t cur = pos;
        while (cur < Size() - 1)
        {
            _first[cur] = _first[cur + 1];
            ++cur;
        }
        --_finish;
    }

    size_t Find(T x)
    {
        T*cur = _first;
        while (cur != _finish)
        {
            if (*cur == x)
            {
                return 1;
            }
            else
            {
                ++cur;
            }
        }
        return -1;
    }

    void Print()
    {
        T* cur = _first;
        while (cur != _finish)
        {
            cout << *cur << " ";
            ++cur;
        }
        cout << endl;
    }
private:
    T* _first;
    T* _finish;
    T* _endofstorage;
};

void TestVector()
{
    Vector<int>v1;
    v1.PushBack(1);
    v1.PushBack(2);
    v1.PushBack(3);
    v1.Print();

    Vector<double>v2;
    v2.PushBack(1);
    v2.PushBack(2);
    v2.PushBack(3);
    v2.Print();
}

编译器推演过程如下:
这里写图片描述
汇编层查看:
这里写图片描述
这里写图片描述

5.模板参数-实现容器适配器


使用容器适配器实现复用
实现栈:

template<typename T>

class SeqList
{

private:
    T* _data;
    int _size;
    int _capacity;

};

template<class T,class Container=SeqList<T>>

class Stack
{
public:
    void Push(const T& x);
    void Pop();
    const T& Top();
    bool Empty();
private:
    Container _con;
};

void TestContainer()
{
    Stack<int> s1;
    Stack<int, SeqList<int>>s2;

    Stack<int, SeqList<char>> s3;
}

6.模板的模板参数- -容器适配器


template<typename T>
class SeqList
{
private:
    int _size;
    int _capacity;
    T*  _data;
};
// template <class T, template<class> class Container>
template<class T, template<class> class Container=SeqList>  //  缺省参数
class Stack
{
public:
    void  Push(const T& x);
    void  Pop();
    const T&Top();
    bool  Empty();
private:
    Container<T> _con;
};
void  Test()
{
    Stack<int> s1;
    Stack<int,SeqList<int>>s2;
}

比较5,6的推演过程:
这里写图片描述

7.非类型的类模板参数


//template<typename T, size_t MAX_SIZE>
template<typename T, size_t MAX_SIZE = 10>  //带缺省模板参数
class SeqList
{
public:
    SeqList();
private:
    T _array[MAX_SIZE];
    int _size;
};
template<typename T, size_t MAX_SIZE>
SeqList<T,MAX_SIZE>::SeqList()
:_size(0)
{}
void Test()
{
    SeqList<int> s1;
    SeqList<int,20> s2;
}

注意事项
1 .非类型模板形参:模板的非类型形参也就是内置类型形参,如:template < class T, int a> class B{};其中int a就是非类型的模板形参。
2.非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。
3.非类型模板的形参只能是整型,指针和引用,像double,String, String *这样的类型是不允许的。但是double &,double ,对象的引用或指针是正确的。
4. 调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。
5.注意:任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。
6.全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。
7.sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。
8.当模板的形参是整型时调用该模板时的实参必须是整型的,且在编译期间是常量,比如:template < class T, int a> class A{};如果有int b,这时A< int, b> m;将出错,因为b不是常量,如果const int b,这时A< int, b> m;就是正确的,因为这时b是常量。
9..非类型模板形参的形参和实参间所允许的转换:

a.允许从数组到指针,从函数到指针的转换。如:template < int *a> class A{}; int b[1]; A< b> m;即数组到指针的转换
b.const修饰符的转换。如:template< const int a> class A{}; int b; A<&b> m; 即从int 到const int *的转换。
c.提升转换。如:template< int a> class A{}; const short b=2; A< b> m; 即从short到int 的提升转换
d.整值转换。如:template< unsigned int a> class A{}; A< 3> m; 即从int 到unsigned int的转换

8.非类型的模板函数参数


template <typename T, int VAL> 
T addValue(T const& x) 
{
return x + VAL; 
} 

当我们需要把「函数」或「某种通用操作」作为参数传递时,这一类函数就很有用。例如使用STL(Standard Template Library,标准模板库)时,你可以运用上述 function template 的实例(instantiation),将某值加到元素集内的每一个元素身上:

std::transform (source.begin(), source.end(),  // 来源端起止位置
dest.begin(), // 目的端起始位置
addValue<int,5>); // 实际操作

最后一个自变量将 function template addValue()实例化了,使其操作成为「将5加进一个int 数 值中」。算法transform()会对source 中的所有元素调用这个具现体(函数),然后把结果传入 dest 中。注意上述例子带来的一个问题:addValue< int,5> 是个 function template 实体(instance),而我们知道,所谓「function templates 实体」被认为是命名了一组重载函数集,即使该函数集内可能只有一个函数。根据目前标准,编译器无法借助「重载函数集」来进行 template parameter 的推导。因此你不得不把 function template argument 强制转型为精确类型

std::transform (source.begin(), source.end(),  // 来源端起止位置
dest.begin(), // 目的端起始位置
(int(*)(int const*)) addValue<int,5>); // 操作

9.模板的分离编译


这里写图片描述

解决办法:
1.在模板头文件 xxx.h 里面显示实例化-> 模板类的定义后面添加 template class
SeqList< int >; 一般不推荐这种方法,一方面老编译器可能不支持,另一方面实例化依赖调用者。(不推荐)
2.将声明和定义放到一个文件 ” x x x . hpp” 里面,推荐使用这种方法

模板总结


优点:
1.模板复用了代码,节省资源,更快的迭代开发,C++ 的标准模板库(STL )因此而产生。
2.增强了代码的灵活性。
缺点:
1.模板让代码变得凌乱复杂,不易维护,编译代码时间变长。
2.出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值