PPC的C/C++和人工智能学习笔记
每一篇学习笔记,都只是为了更好地掌握和理解

C++语言基础(15)_模板

今天学习C++的模板。

 

C++模板定义模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数, 从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。

 

模板,它保留共性,能改写特性。是C++支持类型参数化的多态性工具(函数重载不能说成多态)。是C++语言中常用的一种高级泛型编程技术。

 

C++有函数重载,函数名相同,但是参数列表不同可以构成重载。函数重载常用来实现功能类似而所处理的数据类型不同的问题。但是,函数重载其实是有多少种数据类型就要写多少次的(关键是还有自定义数据类型!),用起来还是感觉不是很方便,C++引入了模板的概念,能够是代码重用效率更高,更加方便。

 

函数模板:(把函数参数的类型再次做为参数)

template <class T> //class也可以用typename替代

返回类型 函数名(形参表)

{ 函数体 }

说明:template是声明模板的关键字,表示声明一个模板;<>是类型参数列表;class(或者typename)关键字,相当于修饰类型;T是类型名,它是标识符,满足标识符的命名规则和命名规范(也就是可以自己起名字,比如T1)。而且可以有多个参数,比如:template <class T1, class T2>。注意:后面不能跟分号。

 

例子:

template <class T> //函数模板

T add(T a, T b) {

return a + b;

}

int main() {

add(1, 2); //可以,隐式类型传参

add(1.0, 2.0); //可以

//add(1, 2.0); //不行,自动去匹配类型的时候出现了歧义

add<int>(1, 2.0); //显式类型传参

return 0;

}

上面这个例子,就是用函数模板来代替所有的重载。所以说:函数模板实现了函数参数的通用性,简化了重载函数的设计。这是一种抽象编程方式,通过模板,我们书写代码是不确定类型的。假如所有参数类型的重载函数体一样,我们可以用函数模板,假如不同就不行了。

函数模板的调用,大部分情况和普通函数调用是一样的,原因在于函数带参数。

参数带类型,模板调用如果通过实参能确定T这个类型,那么和普通函数的调用没区别。其实就是隐式调用。

如果通过实参无法确实T类型,需要显式给出模板参数类型。

隐式类型传参:先通过实参得到类型T,调用函数模板实例化出该类型的模板函数,再调用该函数。

显示类型传参:先直接给出该类型的模板函数,再调用该函数。

 

函数模板和模板函数:

函数模板是抽象的,不能直接调用,只能通过确定类型T再实例化出模板函数,然后才能调用。函数模板实例化,是在程序运行时候进行的。所以才有用模板效率低的说法。

 

函数模板的重载:

函数模板可以进行重载,只要满足重载的要求:

参数列表不同,有两个参数列表,一个形参列表,一个参数类型列表,只要有一个列表不同,就构成重载;

还可以和普通同名函数构成重载;

优先调用普通函数(非模板函数),如果找不到参数完全匹配的,编译器检查可否用函数模板去生成完全符合参数类型的模板函数。如果也不能匹配,又检查是否可以通过类型转换去匹配普通函数。

 

参数类型是指针:

不习惯把指针全部用T代表,而是把指针指的类型用T代表,引用也是。原因是行为体的书写方便。

template <class T>

T * getVal(T * a){

return a;

}

 

类型参数表里面可以给出普通形参:

template <class T,int c> //函数模板

void show(T a) { cout << c << endl; }

show<int, 2>(1); //调用方式必须如此,必须是显式调用。

 

类模板:(类里面的某些类型作为参数)

template <typename T>

class CMyClass { T buff[100]; }

如此,这个数组就可以是任意类型了。

 

还可以加上普通参数:

template <class T,size_t LENGTH>

class CMyClass { T buff[LENGTH]; }

CMyClass<int,10>  mm; //创建对象这样写,模板类和模板函数不一样,类没有参数列表,无法推断类型参数,所以必须是显式地给出参数类型

 

模板类的成员函数:

template <typename T>

class CMyStack

{

T buff[100];

public:

void SetVal(T const& srcVal);

T const& GetVal() const

{

return buff[0];

}

};

 

template <typename T> //这里也必须加上

void CMyStack<T>::SetVal(T const& srcVal)

{

buff[0] = srcVal;

}

 

与函数模板一样,类模板也必须生成模板类才能使用。

 

注意:类模板在单独文件中书写的时候,必须把所有的成员函数的定义也写在头文件.h中,而不能写在单独的cpp文件中。原因是c++编译的时候,cpp文件是独立编译的。

 

用类模板写一个支持多种数据类型的栈:

//MyStack.h 类模板必须全部写在这里,不能用.cpp
//尝试用 类模板写一个 栈
#pragma once
template <typename T, int LENGTH>
class CMyStack
{
public:
 CMyStack();
 ~CMyStack();
 void push(T const& val);
 T pop();
 bool isEmpty() const;
 bool isFull() const;
private:
 T m_buff[LENGTH];
 int m_length;
};

template <typename T, int LENGTH>
bool CMyStack<T, LENGTH>::isFull() const
{
 return m_length >= LENGTH;
}

template <typename T, int LENGTH>
bool CMyStack<T, LENGTH>::isEmpty() const
{
 return m_length == 0;
}

template <typename T, int LENGTH>
T CMyStack<T, LENGTH>::pop()
{
 return m_buff[--m_length];//这个代码会有一个隐患,可能越界
 //if (m_length > 0)
 // return m_buff[--m_length];
 //else
 // throw "null";//两种处理方式,一种只能抛异常,一种是什么都不做
}

template <typename T, int LENGTH>
void CMyStack<T, LENGTH>::push(T const& val)
{
 m_buff[m_length++] = val;
}

template<typename T, int LENGTH>
CMyStack<T, LENGTH>::CMyStack()
{
 //下面注释的写法不合适,原因T类型不知道
 //for (size_t i = 0; i < LENGTH; ++i)
 //{
 // m_buff[i] = 0;
 //}
 //两种做法,第一种不确定,什么都不做。第二种内存直接逐字节给0值
 memset(m_buff, 0, sizeof(T)* LENGTH);
 m_length = 0;
}

template<typename T, int LENGTH>
CMyStack<T, LENGTH>::~CMyStack()
{

}

(2017-04-10 www.vsppc.com)

学习笔记未经允许不得转载:PPC的C/C++和人工智能学习笔记 » C++语言基础(15)_模板

分享到:更多 ()

评论 70

评论前必须登录!