一种用于实现 通用编程 的机制。
将 数据类型 可以作为参数进行传递 。
通过使用模板,我们可以编写可复用的代码,可以适用于多种数据类型。
c++模板的语法使用尖括号 < >
来表示泛型类型,并使用关键字 template
来定义和声明模板。
示例:
#include
using namespace std;
// 定义一个模板函数,此处 typename 可以替换成 class 二者作用相同
template<typename T>
T add(T a, T b)
{
return a + b;
}
int main(int argc, char *argv[])
{
int x = 5, y = 10;
float f1 = 2.5, f2 = 3.7;
// 调用模板函数
std::cout << "Sum of integers: " << add(x, y) << std::endl;
std::cout << "Sum of floats: " << add(f1, f2) << std::endl;
return 0;
}
编译器并不是把函数模板处理成能够处理任何类型的函数;
函数模板通过具体类型产生不同的函数
编译器会对函数模板进行
两次编译
:
- 在
声明
的地方对模板代码本身
进行编译;- 在
调用
的地方对参数替换后的代码
进行编译
函数模板
类模板
- 声明
- 使用
//class 和 typename 都是一样的,用哪个都可以
template<class 模板名,class 模板名,...>
//或
template<typename 模板名,typename 模板名,...>
注意:
位置:函数上
在该函数
任何一处
使用数据类型
的地方都可以使用模板名替换
。
- 函数模板 可以
自动推导参数的类型
,但是不会进行类型转换
- 函数模板 可以自动类型推导,
也可以显式指定类型
- 只能在 声明的 所在函数中使用
#include
using namespace std;
template<class X>
void add(X a, X b)
{
cout << "a+b=" << a + b << endl;
}
void add02(int a, int b)
{
cout << "a+b=" << a + b << endl;
}
int main(int argc, char *argv[])
{
add(10, 20); //a+b=30
add(10.2, 20.3); //a+b=30.5
add('a', 'b'); //a+b=195
//模板可以推导其对应的数据类型,但是无法进行自动转换
//add(1, 'a'); // 报错
//普通函数可以进行自动类型转换
add02(1, 'a'); //a+b=98
//模板可以显示替换
add<int>(1,2); //a+b=3
add<int>(1,'a'); //a+b=3
return 0;
}
(1)、函数模板不允许自动类型转化,普通函数能够自动进行类型转;
示例:
同上面示例
(2)、函数模板和普通函数同时识别,优先使用普通函数
,加<>强制使用函数模板
示例:
template<typename T>
void func(T a, T b){
cout<<"函数模板"<<endl;
}
//普通函数
void func(int a, int b){
cout<<"普通函数"<<endl;
}
void test02()
{
//普通函数
func(10, 20);
//显示调用函数模板
func<>(10, 20);
}
(3)、函数模板可以使用<>,普通函数不行
在函数模板的数据类型 为对象的时候,打印对象会报错,因为 没有 重载<< 运算符
解决方案:
示例:
#include
#include
using namespace std;
template <class X>
void print(X x)
{
cout << x << endl;
}
class Person{
// friend ostream& operator <<(ostream& out,Person& p);
friend void print<Person>(Person p);
private:
char name[50];
int age;
public:
Person(){}
Person(char *name,int age){
strcpy(this->name,name);
this->age = age;
}
};
//1、重载运算符
//ostream& operator <<(ostream& out,Person& p)
//{
// out << p.name << "\t" << p.age << endl;
// return out;
//}
//2、函数模板具体化
template<> void print<Person>(Person p)
{
cout << p.name << "\t" << p.age << endl;
}
int main(int argc, char *argv[])
{
print(10);
Person p("张三",18);
print(p);
return 0;
}
//10
//张三 18
同函数模板
在类上
用法:
类名<实际数据类型1,实际数据类型2,...> 对象名(实参列表);
注意:
- 类模板
实例化对象
是不能自动推导类型
,需指定
示例:
#include
#include
using namespace std;
template<class X,class Y>
class Data{
private:
X x;
Y y;
public:
Data(){}
Data(X x,Y y)
{
this->x = x;
this->y = y;
}
void print()
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
}
};
int main(int argc, char *argv[])
{
//类模板创建对象
Data<int,char> d01(10,'a');
d01.print();
Data<string,int> d02("张三",18);
d02.print();
// 类模板创建对象时必须说明其模板的数据类型
// Data d03("张三",18); //报错
// d03.print();
return 0;
}
//x = 10
//y = a
//x = 张三
//y = 18
示例:
#include
#include
using namespace std;
template<class X,class Y>
class Data{
private:
X x;
Y y;
public:
Data(){}
Data(X x,Y y)
{
this->x = x;
this->y = y;
}
void print()
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
}
};
//子类继承与类模板,要么说明其模板的数据类型
class Content:public Data<int,int>{
public:
Content(){}
Content(int x,int y):Data(x,y){}
};
//要么自己也是类模板
template <class X,class Z>
class Context:public Data<X,Z>{
public:
Context(){}
Context(X x,Z y):Data<X,Z>(x,y){}
};
int main(int argc, char *argv[])
{
Content c01(10,20);
c01.print();
Context<string,string> c02("德玛","男");
c02.print();
return 0;
}
//x = 10
//y = 20
//x = 德玛
//y = 男
注意:外部实现函数时也需定义与类相同的函数模板
示例:
#include
using namespace std;
template<class X,class Y>
class Data{
private:
X x;
Y y;
public:
Data();
Data(X x,Y y);
void print();
};
//外部实现,也需要定义类的模板
template<class X,class Y>
Data<X,Y>::Data()
{
}
template<class X,class Y>
Data<X,Y>::Data(X x,Y y)
{
this->x = x;
this->y = y;
}
template<class X,class Y>
void Data<X,Y>::print()
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
}
int main(int argc, char *argv[])
{
Data<int,double> d01(10,20.0);
d01.print();
return 0;
}
//x = 10
//y = 20
在实例化模板之前,编译器对模板的定义体是不处理的
在实例化模板时编译器必须在上下文中可以查看到其定义实体
因此模板的实例化与定义体必须放到同一文件中。
但是如果文件名为.h皆为是头文件,在头文件中定义函数不符合头文件的将声明与实例分开的规则
顾将头文件名改为hpp,并在前内部进行示例化
data.hpp
头文件
#include
using namespace std;
template<class X,class Y>
class Data{
private:
X x;
Y y;
public:
Data();
Data(X x,Y y);
void print();
};
template<class X,class Y>
Data<X,Y>::Data()
{
}
template<class X,class Y>
Data<X,Y>::Data(X x,Y y)
{
this->x = x;
this->y = y;
}
template<class X,class Y>
void Data<X,Y>::print()
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
}
main.cpp
#include
#include "data.hpp"
using namespace std;
int main(int argc, char *argv[])
{
Data<int,double> d01(10,20.0);
d01.print();
return 0;
}
//x = 10
//y = 20
注意:该函数必须是类模板的友元函数
类模板的对象作为参数,数据类型写死,就是int,char等,
将该函数也创建成与类模板相同的函数模板,函数的参数是 同类模板的数据类型的类模板对象
示例:
#include
using namespace std;
template<class X,class Y>
class Data{
//
// friend void showData(Data & data);
template<class Z,class W>
friend void showData(Data<Z,W> & data);
private:
X x;
Y y;
public:
Data(){}
Data(X x,Y y){
this->x = x;
this->y = y;
}
};
//类模板的对象作为参数,数据类型写死,就是int,char,
//弊端:在调用该函数时,传递的对象的数据类型就只能是写死的类型
//void showData(Data & data)
//{
// cout << data.x << "\t" << data.y << endl;
//}
//将该函数也创建成与类模板相同的函数模板,
//函数的参数是 同类模板的数据类型的类模板对象
template<class X,class Y>
void showData(Data<X,Y> & data)
{
cout << data.x << "\t" << data.y << endl;
}
int main(int argc, char *argv[])
{
Data<int,char> d01(100,'a');
showData(d01); //100 a
Data<int,double> d02(100,1.0);
showData(d02); //100 1
return 0;
}
示例:数组长度可变,可以存储基本类型数据,也可以存储对象
arraylist.hpp
头文件
#include
#include
#include
using namespace std;
template<class T>
class ArrayList{
private:
//记录数组的地址
T *data;
//记录已存数组的个数
int size;
//记录可容纳数组的个数
int count;
public:
ArrayList();
~ArrayList();
//添加数据函数
void add(T t);
//获取集合函数
T& get(int index);
//获取集合长度
int getSize();
};
//实现无参构造
template<class T>
ArrayList<T>::ArrayList()
{
this->count = 2;
this->size = 0;
this->data = new T[count];
}
//实现析构
template<class T>
ArrayList<T>::~ArrayList()
{
if(data != NULL)
{
delete [] data;
data = NULL;
}
}
//实现添加数据函数
template<class T>
void ArrayList<T>::add(T t)
{
if(size == count)
{
//满了,扩容
count = count * 2;
T *newData = new T[count];
memcpy(newData, data, size*sizeof(T));
delete [] data;
data = newData;
}
data[size] = t;
size++;
}
//实现获取数据函数
template<class T>
T& ArrayList<T>::get(int index)
{
return data[index];
}
//实现获取集合长度函数
template<class T>
int ArrayList<T>::getSize()
{
return size;
}
main.cpp
#include
#include "arraylist.hpp"
using namespace std;
void fun01()
{
ArrayList<int> a1;
a1.add(1);
a1.add(3);
a1.add(5);
a1.add(7);
a1.add(9);
int size = a1.getSize();
cout << "长度为:" << size << endl;
for(int i = 0; i < size; i++)
{
cout << a1.get(i) << endl;
}
}
class Person{
private:
char *name;
int age;
public:
Person(){}
Person(char *name, int age)
{
//指针直接赋值
this->name = name;
//字符串指针不可以使用strcpy,字符串指针存储的是字符串的首地址,使用strcpy会出现内存污染
// strcpy(this->name, name);
this->age = age;
}
~Person(){}
void print_info()
{
cout << "姓名: " << name << "\t年龄:" << age << endl;
}
};
void fun02()
{
Person p1("张三", 18);
Person p2("李四", 13);
Person p3("王五", 13);
Person p4("钱⑥", 12);
// p1.print_info();
ArrayList<Person> a1;
a1.add(p1);
a1.add(p2);
a1.add(p3);
a1.add(p4);
int size = a1.getSize();
cout << "长度为:" << size << endl;
for(int i = 0; i < size; i++)
{
a1.get(i).print_info();
}
}
int main(int argc, char *argv[])
{
fun01();
fun02();
return 0;
}
//长度为:5
//1
//3
//5
//7
//9
//长度为:4
//姓名: 张三 年龄:18
//姓名: 李四 年龄:13
//姓名: 王五 年龄:13
//姓名: 钱⑥ 年龄:12
上行转换
下行转换
语法: (转换后的类型)要转换的数据或变量
语法:
static_cast(要转换的数据)
示例:
//基本类型转换 支持
int num = static_cast<int>(3.14f);
//基本指针类型转换 不支持
float f=0.0f;
//int *p1 = static_cast(&f);
//上行转换 支持(安全)
Base *p2 = static_cast<Base *>(new Son);
//下行转换 支持(不安全)
Son *p3 = static_cast<Son *>(new Base);
//不相关类型转换 不支持
//Son *p4 = static_cast(new Other);
语法:
dynamic_cast(要转换的数据)
示例:
//基本类型转换 不支持
//int num = dynamic_cast(3.14f);
//基本指针类型转换 不支持
float f=0.0f;
//int *p1 = dynamic_cast(&f);
//上行转换 支持(安全)
Base *p2 = dynamic_cast<Base *>(new Son);
//下行转换 不支持(不安全)
//Son *p3 = dynamic_cast(new Base);
//不相关类型转换 不支持
//Son *p4 = dynamic_cast(new Other);
语法:
const_cast
注意:
只能对指针与引用的变量使用
示例:
//将非const 转换成 const
int num = 10;
const int *p1 = const_cast<const int *>(&num);
//将const 转换成 非const
const int data=0;
int *p = const_cast<int *>(&data);
简介:
这是最不安全的一种转换机制,最有可能出问题。
主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针.
语法:
reinterpret_cast
示例:
//基本类型转换 不支持
//int num = reinterpret_cast(3.14f);
//基本指针类型转换 支持
float f=0.0f;
int *p1 = reinterpret_cast<int *>(&f);
//上行转换 支持(安全)
Base *p2 = reinterpret_cast<Base *>(new Son);
//下行转换 支持(不安全)
Son *p3 = reinterpret_cast<Son *>(new Base);
//不相关类型转换 支持
Son *p4 = reinterpret_cast<Son *>(new Other);