模板
简介:
一种用于实现通用编程的机制。
通过使用模板我们可以编写可复用的代码,可以适用于多种数据类型。
C++模板的语法使用角括号 < > 来表示泛型类型,并使用关键字 template 来定义和声明模板
概念:
c++范式编程
特点:
模板引入:
#include <iostream>
using namespace std;
// 模板的引入
// 想打印任何接收的类型 就得写所有类型的函数进行接收
void print(char a)
{cout<<a<<endl;
}
void print(int a)
{cout<<a<<endl;
}
void print(double a)
{cout<<a <<endl;
}int main(int argc, char const *argv[])
{print('a');print(1);return 0;
}
模板的使用:
#include <iostream>
using namespace std;
template<class z>
//模板的定义 自己会推导你写入的是什么 将a的类型换成什么
void print(z a)
{cout<< a <<endl;
}
int main(int argc, char const *argv[])
{print('a');print(10);print("你好");return 0;
}
模板函数:
语法:
template <class 假设的类型1 ,class 假设的类型2,...>返回值类型 函数名(形参列表) {函数体; }
注意:
当前函数中任何一处使用数据类型的地方都可以使用假设的类型
例子:
特点:
- 1 函数模板可以自动推导参数的类型,但是不会进行类型转换
- 2 函数模板可以自动类型推导,也可以显式指定类型
- 显式指定类型
- 函数名<指定的类型1,指定的类型2,…>(实参列表);
- 3,只能在声明的所在函数中使用
补充:
函数模板会编译两次:
1,在加载时对函数模板进行第一次编译
2,在调用时推导T的类型再次将函数模板编译为模板函数
模板函数与普通函数的区别
- 1,函数模板不允许自动类型转化 普通函数能够自动进行类型转
- 2,函数模板和普通函数同时识别,优先使用普通函数,加<>强制使用函数模板
- 3,函数模板可以使用<>,普通函数不行
模板函数 局限性:
#include <iostream> using namespace std; // 函数模板的局限性 template <class T> void method(T t) {cout << t << endl; } class A { }; int main(int argc, char const *argv[]) {method(10);A a;method(a); // 此时模板可以推导出T的类型为A,但是A类没有重载<<运算符,所以// 无法通过cout输出, 此时语法无错, 但是无法编译生成可执行文件 return 0; }解决方案1 : 重载 << 运算符 #include <iostream>using namespace std; template <class T> void method(T t) {cout << t << endl; } class A { }; ostream &operator<<(ostream &out, A &a) {out << "打印A的对象" << endl;return out; } int main(int argc, char const *argv[]) {method(10);A a;method(a);return 0; } 解决方案2 : 指定模版函数 #include <iostream>using namespace std; template <class T> void method(T t) {cout << t << endl; } class A { }; // 指定模版函数 template <> void method<A>(A a) {cout << "打印A的对象" << endl; } int main(int argc, char const *argv[]) {method(10);A a;method(a);return 0; }
类模板
概念:
- 有模板的类
语法:
template<class 假设的类型1,class 假设的类型2,....>
{}
作用:
当前类中任何一处使用数据类型的地方都可以使用假设的类型
创建对象
类名 <类型1,类型2,...> 对象名(实参列表); 类名 <类型1,类型2,...> *对象名 = new类名<类型1,类型2>(实参列表);
模板类作为父类
- 方案1 子类指明父类模板类型
- 方案2 子类也是模板类
模板类的函数声明与实现分离
注意:
- 每一个 类外实现的函数都是模板函数
template <class 假设的类型>返回值类型 类名<><假设的类型>::函数名(形参列表){ }
#include <iostream> using namespace std; template <class Q> class Data { private:Q q;public:Data();Data(Q q);Q getQ();void setQ(Q q); }; template <class X> Data<X>::Data() { } template <class Q> Data<Q>::Data(Q q) {this->q = q; } template <class Q> Q Data<Q>::getQ() {return q; } template <class Q> void Data<Q>::setQ(Q q) {this->q = q; } int main(int argc, char const *argv[]) {Data<int> data(10);return 0; }
hpp文件
因为模板类的声明与实现无法分离,故将模板类的声明与实现在同一文件中。该文件的后缀名为 .hpp
示例:
- data.hpp
template <class Q> class Data { private:Q q;public:Data();Data(Q q);Q getQ();void setQ(Q q); }; template <class X> Data<X>::Data() { } template <class Q> Data<Q>::Data(Q q) {this->q = q; } template <class Q> Q Data<Q>::getQ() {return q; } template <class Q> void Data<Q>::setQ(Q q) {this->q = q; }
main.cpp
#include <iostream> #include "data.hpp" using namespace std; int main(int argc, char const *argv[]) {Data<int> data(10);return 0; }
编译命令
g++ main.cpp
类模板对象作为形参
#include <iostream> #include "../15_code/data.hpp" using namespace std; // 指明类型 // void print(Data<int>& data) // { // cout << "xxx" << endl; // } // void print(Data<char>& data) // { // cout << "YYY" << endl; // } // 函数模板 template <class E> void print(Data<E> &data) {cout << "xxx" << endl;} int main(int argc, char const *argv[]) {Data<int> data(10);print(data);Data<char> data02('A');print(data02);return 0; }
自定义集合
作用:
存储一组数据类型相同的数据的容器
特点
可以存储任何一种数据类型 基本类型和自定义类型
#include "array.hpp" class Person { private:char *name;public:Person(){this->name = NULL;}Person(char *name){// 测试存储效果cout << name << "被创建" << endl;int len = strlen(name);this->name = (char *)calloc(len + 1, 1);strcpy(this->name, name);}Person(const Person &pe){// if (name != NULL)// {// // free(name); 有时候会是野指针 释放野指针就崩了// name = NULL;// }int len = strlen(pe.name);this->name = (char *)calloc(len + 1, 1);strcpy(this->name, pe.name);}// 析构~Person(){cout << name << "被释放了" << endl;} // 不用释放因为 头中已经释放过了char *getName(){return name;} }; int main(int argc, char const *argv[]) {// 实验基本数据类型 char sort int float double long bool// 实验存 intArrayList<int> nums;cout << "实验 int 型" << endl;nums.add(1);nums.add(2);nums.add(3);nums.add(4);nums.add(5);for (int i = 0; i < nums.getlen(); i++){cout << nums.get(i) << endl; // 按下标获取}// 实验存 char 型ArrayList<char> cs;cs.add('A');cs.add('b');cs.add('c');cs.add('d');cs.add(65);cout << "实验 char 型" << endl;for (int i = 0; i < cs.getlen(); i++){cout << cs.get(i) << endl; // 按下标获取}// 实验long型ArrayList<long> lo;lo.add(123123123L);cout << "实验 long 型" << endl;for (int i = 0; i < lo.getlen(); i++){cout << lo.get(i) << endl; // 按下标获取}// 测试bool型ArrayList<bool> bol;bol.add(true);bol.add(false);cout << "实验 bool 型" << endl;for (int i = 0; i < bol.getlen(); i++){cout << bol.get(i) << endl; // 按下标获取}// 测试string型ArrayList<string> str;str.add("铁锤打铁");str.add("云边有个小卖部");cout << "实验 bool 型" << endl;for (int i = 0; i < str.getlen(); i++){cout << str.get(i) << endl; // 按下标获取}cout<<"\n"<<endl;// 测试自定义类型ArrayList<Person> ps;ps.add(Person("哇嘎"));ps.add(Person("铁头"));ps.add(Person("铁锤"));cout << "\n";cout << "遍历后的结果" << endl;for (int i = 0; i < ps.getlen(); i++){cout << ps.get(i).getName() << endl;}return 0; }
类型转换
1, C提供的强制转换
语法:(转换后的类型)要转换的数据或变量
2,静态转换
语法:
//基本类型转换 支持 int num = static_cast<int>(3.14f); //基本指针类型转换 不支持 float f=0.0f; //int *p1 = static_cast<int *>(&f); //上行转换 支持(安全) Base *p2 = static_cast<Base *>(new Son); //下行转换 支持(不安全) Son *p3 = static_cast<Son *>(new Base); //不相关类型转换 不支持 //Son *p4 = static_cast<Son *>(new Other);
3,动态类型转换
语法:
dynamic_cast<T> (要转换的数据)
示例
//基本类型转换 不支持 //int num = dynamic_cast<int>(3.14f); //基本指针类型转换 不支持 float f=0.0f; //int *p1 = dynamic_cast<int *>(&f); //上行转换 支持(安全) Base *p2 = dynamic_cast<Base *>(new Son); //下行转换 不支持(不安全) //Son *p3 = dynamic_cast<Son *>(new Base); //不相关类型转换 不支持 //Son *p4 = dynamic_cast<Son *>(new Other);
4,常量转换
语法 : **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);
5,重新解释转换
简介
这是最不安全的一种转换机制,最有可能出问题。 主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针
语法
reinterpret_cast<T>
示例
//基本类型转换 不支持 //int num = reinterpret_cast<int>(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);
总结:
1,强制转换 2,系统提供的函数进行转换静态转换基本类型 支持子转父 支持父转子 支持不相干 不支持动态转换基本类型 不支持子转父 支持父转子 不支持不相干 不支持常量转换常量转非常量非常量转常量重新解释基本类型 不支持子转父 支持父转子 支持不相干 支持