模板
简介:
一种用于实现通用编程的机制。
通过使用模板我们可以编写可复用的代码,可以适用于多种数据类型。
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 // 实验存 int ArrayList<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,系统提供的函数进行转换 静态转换 基本类型 支持 子转父 支持 父转子 支持 不相干 不支持 动态转换 基本类型 不支持 子转父 支持 父转子 不支持 不相干 不支持 常量转换 常量转非常量 非常量转常量 重新解释 基本类型 不支持 子转父 支持 父转子 支持 不相干 支持