模板-初阶

模板

模板分函数模板和类模板

模板也是针对C语言不足诞生的机制。模板更贴切的名称叫泛型编程——即不在针对某一具体类型,而是能适应广泛的类型。

函数模板

比如交换函数,不在针对int类型来设置交换,而是能让广泛的类型进行交换。

c++之前的不同类型交换函数写要重复太多次:

void Swap(int& left, int& right)

{

    int temp = left;

    left = right;

    right = temp;

}

void Swap(double& left, double& right)

{

    double temp = left;

    left = right;

    right = temp;

}

所以设计了一种语法,建立一个模板,编译器对照模板自己生成对应的函数。于是诞生了一个关键字:template(模板)

然后选择下面两种格式中的一个写

template<class T>

template<typename T>

class 和 typename目前没有区别,class和typename都是关键字,但是不能这样:template<struct T>。因为struct要兼容C语言的结构。

template<typename T>

void Swap(T& left, T& right)

{

    T tmp = left;

    left = right;

    right = tmp;

}

一个简单的交换函数模板就写好了

简单应用:

int main()

{

    int a = 1, b = 2;

    double c = 2.2, d = 3.3;

    Swap(a, b);

    Swap(c, d);//两个swap函数不是同一个函数,按f11都会进入模板是编译器优化的结果

    return 0;

}

模板的本质:调用什么类型就会根据对应的类型生成一个对应的函数

根据实参类型决定形参类型的过程叫编译器的推演,推演后生成函数的过程叫模板的实例化。

通过汇编代码发现两个函数的参数和地址都不一样

swap是库中的模板,可以直接用

函数模板类型一般是编译器通过形参传递给实参推演出来的,也存在特殊情况

template<class T>

T* func(int n)

{

    return new T[n];

}

int main()

{

    //int* p = func(10);这样编译器没法推演T的类型

    int* p1 = func<int>(10);

    double* p2 = func<double>(10);

    return 0;

}

如果不能推演,那么需要显式实例化,指定模板参数。

一个模板下只能跟一个函数,不能一个模板下更多个函数。

模板函数的性质和普通函数一样,可以有缺省值;模板参数也是可以有缺省参数的。比如:template<class T = char>

模板参数——传递的是类型

函数参数——传递的是对象值

类模板

C语言数据结构可以通过typedef来改变存储的类型,但是也存在硬伤。当需要两个数据结构来存储不同的类型时就存在问题了。typedef修改类型,类型都会改。

typedef int StackDateType

class Stack

{

public:

    Stack(int capacity = 0)

    {

        _p = new StackDateType[capacity];

        _capacity = capacity;

        _top = 0;

    }

    Push(StackDateType x)

    {

        ……

    }

private:

    StackDateType* _p;

    int _capacity;

    int _top

};

int mian()

{

    Stack st1;

    Stack st2;//如果st1存储int类型的数据,st2存储double类型的数据要怎么改?

    return 0;

}

所以需要用到类模板,类模板的定义过程也很简单

template<class T>//T只是类名,可以任意取

class Stack

{

public:

    Stack(int capacity = 0)

    {

        _p = new T[capacity];

        _capacity = capacity;

        _top = 0;

    }

    Push(T x)

    {

        ……

    }

private:

    T* _p;

    int _capacity;

    int _top

};

int mian()

{

    Stack<int> st1;

    Stack<double> st2;//函数模板可以根据实参推演参数类型,类模板没办法推演,所以要指定存储类型

    return 0;

}

函数模板和类模板的声明和定义可以分离

函数模板:

template<typename T>

void Swap(T& left, T& right);//声明

template<typename T>//在定义的地方,模板要再写一遍

void Swap(T& left, T& right)//定义

{

    T tmp = left;

    left = right;

    right = tmp;

}

类模板:类模板主要是类的成员函数声明和定义分离

template<class T>

class Vector

{

public :

    Vector(size_t capacity = 10);//成员函数的声明

    void PushBack(const T& data);

    void PopBack();

T& operator[](size_t pos)

{

    assert(pos < _size);

    return _pData[pos];

}

private:

    T* _pData;

    size_t _size;

    size_t _capacity;

};

template<class T>//在定义的地方,模板要再写一遍

Vector<T>::Vector(size_t capacity = 10)//定义的地方要声明所在类域,声明的方式为:Vector<T>::

    : _pData(new T[capacity])

    , _size(0)

    , _capacity(capacity)

{}

声明和定义分离,每个定义的前面都要加上template<class 参数名>或者template<typename 参数名>

一般声明和定义分离更规范。

template<class T>

T Add(const T& left, const T& right)

{

    return left + right;

}

int main()

{

    int a1 = 10, a2 = 20;

    double d1 = 10.0, d2 = 20.0;

    Add(a1,a2);

    Add(d1,d2);

    return 0;

}

这是一个正常编译的相加模板

但如果这样使用模板就会报错:Add(a1,d1);因为编译器不知道T是什么类型,T不能既是int又是double。

非要这么写的话,要指定T的具体类型:Add<int>(a1,d1);或者Add<double>(a1,d1);

这样写会报警告,被动转换(隐式类型转换)可能会丢失数据

或者主动转换(强转):Add(a1,(int)d1);或者Add((double)a1,d1);

int Add(const int& left, const int& right)

{

    return left + right;

}

template<class T>

T Add(const T& left, const T& right)

{

    return left + right;

}

int main()

{

    return 0;

}

模板和函数能同时存在吗?

可以

如果存在Add(1,2);语句,会调用模板函数还是一般函数?

一般函数。编译器调用时,有对应函数就调用对应函数,没有对应函数才会通过模板转换。

如果非要使用模板转换的函数呢?

要指定显式实例化:Add<int>(1, 2);

模板在哪个阶段处理?预处理?编译?汇编?

多个模板参数的情况:

template<class K, class V>

void Size(const K& key, const V& value)

{

    cout << sizeof(key) << endl;

    cout << sizeof(value) << endl;

}

int main()

{

    Size(1, 1);

    Size(1, 1.1);

    Size<int, char>(1, 'A');//多个模板参数也可以指定实例化参数

    return 0;

}

多个模板参数就可以推出不同类型了。

模板参数也可以有缺省值,但是要在模板函数没有参数的情况下

template<class K, class V = double>//模板参数的缺省和一般函数一样,要从右往左缺省,因为函数实参是从左往右赋值的

void Size(const K& key, const V& value)

{

    cout << sizeof(key) << endl;

    cout << sizeof(value) << endl;//打印出1

}

int main()

{

    Size<int>(1, 'A');

    return 0;

}

在模板函数有参数的情况下,还是会根据实参来推导类型

模板函数没有参数时:

template<class K, class V = double>

void Size()

{

    cout << sizeof(K) << endl;//打印出4

    cout << sizeof(V) << endl;//打印出8

}

int main()

{

    Size<int>();

    return 0;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值