C++ | C++模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
模板是创建泛型类或者函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,使用的是模板的概念。
每个容器都有一个单一的定义,比如向量,我们可以定义许多不同类型的向量,比如vector
int> 或vector
string>。
函数模板
// type是函数所使用的数据类型的占位符名称,这个名称可以在函数定义中使用。
template ret-type func-name(parameter list)
{
// 函数主体
}
:类型参数可以有多个,以逗号’,
’分隔,类型参数列表以
包围。
函数名(形参列表):ret-type func-name(parameter list)
;
实例1(函数重载):
/*******************************************************************
* > File Name: func-overload.cpp
* > Create Time: 2021年09月20日 9:27:47
******************************************************************/
#include
using namespace std;
inline int const& MAX(int const& a, int const& b)
{
return a }
inline float const& MAX(float const& a, float const& b)
{
return a }
inline char const& MAX(char const& a, char const& b)
{
return a }
inline bool const& MAX(bool const& a, bool const& b)
{
return a }
int main(int argc, char* argv[])
{
int i = 10;
int j = 20;
cout
float d1 = 20.01;
float d2 = 99.999;
cout
bool b1 = true;
bool b2 = false;
cout
char s1 = 'A';
char s2 = 'a';
cout
return 0;
}
编译、运行:
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> make
g++ -o func-overload func-overload.cpp -g -Wall
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> .func-overload.exe
MAX(i, j): 20
MAX(d1, d2): 99.999
MAX(b1, b2): 1
MAX(s1, s2): a
通过函数重载定义四个名字相同、参数列表不同的函数。本质上来说,定义了三个功能相同、函数体相同的函数,只是数据类型不同而已。
通过函数模板,我们可以将四个函数压缩成一个函数。
数据的值,可以通过函数参数传递,在函数定义时数据的值是未知的,只有等到函数调用时接收了实参才能决定其值,这就是值的参数化。
在C++中,数据的类型也可以通过参数来传递,在函数定义时可以不指定具体的数据类型,当发生函数调用时,编译器可以根据传入的参数自动推断数据类型。这就是类型的参数化。
值(value
)和类型(type
)是数据的两个主要特征,它们在C++中可以被参数化。
函数模板(Function
),实际是建立了一个通用函数,它所用到的数据类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(标识符来占位),等到函数调用时再根据传入的实参来逆推出真正的类型。
typename
关键字
也可以使用class
关键字代替,最开始使用class
来指明类型参数,后续才引入了typename
关键字。
实例2(函数模板):
/*******************************************************************
* > File Name: func-template.cpp
* > Create Time: 2021年09月20日 9:19:33
******************************************************************/
#include
#include
using namespace std;
template /* 模板头 */
inline T const& MAX(T const& a, T const& b) /* 函数头 */
{
return a }
int main(int argc, char* argv[])
{
int i = 10;
int j = 20;
cout
double d1 = 20.01;
double d2 = 99.999;
cout
string s1 = "ABCD";
string s2 = "abcd";
cout
return 0;
}
编译、运行:
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> make
g++ -o func-template func-template.cpp -g -Wall
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> .func-template.exe
MAX(i, j): 20
MAX(d1, d2): 99.999
MAX(s1, s2): abcd
类模板
template
class class-name
{
//TODO:
};
实例3:
/*******************************************************************
* > File Name: template-class.cpp
* > Create Time: 2021年09月20日 10:52:37
******************************************************************/
#include
#include
#include
#include
#include
using namespace std;
// 定义了类Stack,实现了范型方法对元素进行出、入栈的操作
template
class Stack
{
private:
vector elems; // 元素
protected:
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{
return elems.empty();
};
};
template /* 模板头 */
void Stack::push(T const& elem) /* 函数头 */
{
elems.push_back(elem);
}
template /* 模板头 */
void Stack::pop()/* 函数头 */
{
if(elems.empty()){
throw out_of_range("Stack::top(): empty stack");
}
elems.pop_back();
}
template /* 模板头 */
T Stack::top() const/* 函数头 */
{
if(elems.empty()){
throw out_of_range("Stack::top(): empty stack");
}
return elems.back();
}
int main(int argc, char* argv[])
{
try{
Stack intStack;
Stack stringStack;
intStack.push(7);
cout
stringStack.push("hello");
cout stringStack.pop();
stringStack.pop();
}catch(exception const& ex){
cerr return -1;
}
return 0;
}
编译、运行:
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> make
g++ -o template-class template-class.cpp -g -Wall
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> .template-class.exe
7
hello
ExceptionStack::top(): empty stack
实例4:
/*******************************************************************
* > File Name: template-class1.cpp
* > Create Time: 2021年09月20日 13:29:05
******************************************************************/
#include
using namespace std;
template /* 模板头 */
class Point{ /* 声明一个类Point */
public:
Point(T1 x, T2 y):m_x(x), m_y(y){} /* 构造函数 */
public:
T1 getX() const; /* 获取x坐标 */
void setX(T1 x); /* 设置x坐标 */
T2 getY() const; /* 获取y坐标 */
void setY(T2 y); /* 设置y坐标 */
private:
T1 m_x; // x坐标
T2 m_y; // y坐标
};
template /* 模板头 */
T1 Point::getX() const /* 函数头,设置x坐标 */{
return m_x;
}
template
void Point::setX(T1 x){ /* 设置x坐标 */
m_x = x;
}
template
T2 Point::getY() const{ /* 设置y坐标 */
return m_y;
}
template
void Point::setY(T2 y){ /* 设置x坐标 */
m_y = y;
}
int main(int argc, char* argv[])
{
Point p1(10, 20); // 和函数模板不同,在实例化需要显示指明数据类型
cout
Pointp2(10, "东经180度");
cout
/* 赋值号两边都要指明具体的数据类型,且保持一致 */
Point *p3 = new Point("东经180度", "北纬210度");
cout getX() getY()
return 0;
}
编译、运行:
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> make
g++ -o template-class1 template-class1.cpp -g -Wall
template-class1.cpp: 在函数‘int main(int, char**)’中:
template-class1.cpp:49:30: 警告:ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
49 | Pointp2(10, "东经180度");
| ^~~~~~~~~~~
template-class1.cpp:52:57: 警告:ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
52 | Point *p3 = new Point("东经180度", "北纬210度");
| ^~~~~~~~~~~
template-class1.cpp:52:70: 警告:ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
52 | Point *p3 = new Point("东经180度", "北纬210度");
| ^~~~~~~~~~~
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> .template-class1.exe
x = 10, y = 20
x = 10, y = 东经180度
x = 东经180度, y = 北纬210度
实例5(可变数组):
/*******************************************************************
* > File Name: template-class-arr.cpp
* > Create Time: 2021年09月20日 15:10:16
******************************************************************/
#include
#include
using namespace std;
template /* 模板头 */
class Carray{ /* Carray类 */
int size; // 数组元素的个数
T *ptr; // 指向动态分配的数组
public:
Carray(int n = 0); // n表示数组个数
Carray(Carray& a);
~Carray(); /* 析构函数 */
void push_back(const T& a); /* 加入新的元素 */
Carray& operator=(const Carray& a); /* 重载操作符= */
T length() {return size;}; /* 获取数组的大小 */
T& operator[](int i) /* 重载[],支持下标访问 */
{
return ptr[i];
}
};
template
Carray::Carray(int n):size(n)/* 为类成员size赋值为n */
{
if(0 == n){ /* 数组元素为0 */
ptr = NULL;
}else{
ptr = new T[n];/* 申请空间 */
}
}
template
Carray::Carray(Carray& a)
{
if(!a.ptr){ /* a.ptr为NULL,重置可变数组 */
ptr = NULL;
size = 0;
return;
}
ptr = new T[a.size]; /* 申请空间 */
memcpy(ptr, a.ptr, sizeof(T)*a.size); /* 复制a.ptr到ptr */
size = a.size; /* 给size赋值 */
}
template
Carray::~Carray()
{
if(ptr){
delete [] ptr; /* 释放ptr所指向的数组空间 */
}
}
template
Carray & Carray::operator=(const Carray& a)
{
if(this == &a){ // 防止a=a的情况导致出现的错误
return *this;
}
if(a.ptr == NULL){
if(ptr){ /* 不为空 */
delete [] ptr; /* 释放ptr指向的数组空间 */
}
ptr = NULL;
size = 0; /* 让数组的个数为0 */
return *this;
}
if(size if(ptr){/* 不为空 */
delete [] ptr;/* 释放ptr指向的数组空间 */
}
ptr = new T[a.size]; /* 申请空间 */
}
memcpy(ptr, a.ptr, sizeof(T)*a, size); /* 拷贝a.ptr的数据到ptr */
size = a.size;
return *this;
}
template
void Carray::push_back(const T& v)
{
if(ptr)
{
T* tmPtr = new T[size + 1]; /* 重新分配空间 */
memcpy(tmPtr, ptr, sizeof(T)*size); /* 拷贝原数组内容 */
delete []ptr; /* 释放原数组的空间 */
ptr = tmPtr; /* ptr指向新的位置 */
}
else /* 数组本来是空的 */
{
ptr = new T[1];/* 申请一个元素的空间 */
}
ptr[size ++ ] = v; /* 加入新的数组元素 */
}
int main(int argc, char* argv[])
{
Carray a;
for(int i = 0; i a.push_back(i); /* 赋值 */
}
for(int i = 0; i cout }
cout
return 0;
}
编译、运行:
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> make
g++ -o template-class-arr template-class-arr.cpp -g -Wall
PS D:2.SoftToolscygwin64homeflyworkSpacecplusplusday4> .template-class-arr.exe
0 1 2 3 4
强类型语言
强类型语言在定义变量时需要显式地指明数据类型,并且一旦为变量指明了某种数据类型,该变量以后就不能赋予其他类型的数据了,除非经过强制类型转换或隐式类型转换。
弱类型语言
弱类型语言在定义变量时不需要显式地指明数据类型,编译器(解释器)会根据赋给变量的数据自动推导出类型,并且可以赋给变量不同类型的数据。
不管是强类型语言还是弱类型语言,在编译器(解释器)内部都有一个类型系统来维护变量的各种信息。
STL
STL(Standard Template Library,标准模板库)就是 C++ 对数据结构进行封装后的称呼。
C++模板和泛型程序设计
C++模板:函数、结构体、类 模板实现
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net