c++另一种编程思想称为泛型编程,主要利用的技术就是模板。
c++提供两个模板机制,函数模板和类模板
函数模板语法
函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代表
语法:
template
函数声明或定义
template->声明创建模板
typename->表明其后面的符号是一种数据类型,可以用class代替
T ->通用的数据类型,名称可以替换,通常为大写字母
函数模板注意事项
自动类型推导,必须推导出一致的数据类型T,才可以使用
模板必须要确定出T的数据类型,才可以使用
#include
#include
using namespace std;
template<typename T>
void myswap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
template<typename T>
void Mysort(T array[],int len) {
for (int i = 0; i < len; i++) {
int min = i;
for (int j = i + 1; j < len; j++) {
if (array[j] < array[min]) {
min = j;
}
}
if (min != i) {
myswap(array[min], array[i]);
}
}
}
int main() {
char arr[] = "abderf";
Mysort(arr, sizeof(arr) / sizeof(char));
for (int i = 0; i < sizeof(arr) / sizeof(char);i++) {
cout << arr[i] << endl;
}
int arr2[] = { 1,3,7,8,4,5,2 };
Mysort(arr2, 7);
for (int i = 0; i < 7; i++) {
cout << arr2[i] << endl;
}
}
普通函数与函数模板的区别
普通函数调用时可以发生自动类型转换(隐式类型转换)
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换,但如果利用显示指定类型的方式,则可以发生隐式类型转换
普通函数与函数模板的调用规则
1.如果函数模板和普通函数都可以实现,优先调用普通函数
2. 可以通过空模板参数列表来强制调用函数模板
3. 函数模板也可以发生重载
4. 如果函数模板可以产生更好的匹配,优先调用函数模板
#include
#include
using namespace std;
void func(int a,int b) {
cout << a << b << "普通函数" << endl;
}
template<typename T>
void func(T a, T b) {
cout << a << b << "函数模板" << endl;
}
int main() {
int a = 5;
int b = 10;
func(5, 10); // 5 10 普通函数
func<>(a, b); // 5 10 函数模板
char c = 'a';
char d = 'b';
func(a, c); // 5 97 普通函数
func(c, d); // a b 函数模板
}
函数模板会出现,被创建的类来使用的情况,此时便会出现一些问题比如自定义类无法比较大小等等
此时便可以利用具体化的模板,来解决自定义类型的通用化。
```cpp
#include
#include
using namespace std;
class Person {
public:
int age;
string name;
Person(int age, string name) {
this->age = age;
this->name = name;
}
};
template<typename T>
bool compare(T a, T b) {
if (a == b)
return true;
else {
return false;
}
}
template<> bool compare(Person a, Person b) {
if (a.age == b.age && a.name == b.name) {
return true;
}
return false;
}
int main() {
int a = 10;
int b = 10;
bool c = compare(a, b);
if (c == true) {
cout << "相同" << endl;
}
else {
cout << "不同" << endl;
}
Person p1 = Person(20, "lyx");
Person p2 = Person(20, "lyx");
c = compare(p1, p2);
if (c == true) {
cout << "相同" << endl;
}
else {
cout << "不同" << endl;
}
}
类模板作用:
建立一个通用的类,类中的成员和数据类型可以不具体指定,用一个虚拟的类型来代表
语法相同
template
类名
#include
#include
using namespace std;
template<typename Agetype, typename Nametype >
class Person {
public:
Agetype age;
Nametype name;
Person(Agetype a, Nametype n) {
this->age = a;
this->name = n;
}
void showPerson() {
cout << this->age << this->name << endl;
}
};
int main() {
Person<int, string> p = Person<int, string>(10,"lyx");
p.showPerson();
}
类模板和函数模板的区别
类模板没有自动类型推到的使用方式
类模板在模板参数列表中可以有默认参数
ex: template 默认为int类型
类模板中的成员函数创建时机
普通类中的成员函数在一开始就可以创建,但是类模板中的成员函数在调用时才创建
#include
#include
using namespace std;
class Person1 {
public:
void func1() {
cout << "person 1 func" << endl;
}
};
class Person2 {
public:
void func2() {
cout << "person 2 func" << endl;
}
};
template<typename T>
class myclass { //不会报错
public:
T t;
void myfun1() {
t.func1();
}
void myfun2() {
t.func2();
}
};
int main() {
myclass<Person1> p;
p.myfun1();
//p.myfun2(); //调用时才报错
}
类模板对象做参数调用,有三种方法
第一种:指定传入类型
第二种:参数模板化
第三种: 整个类模板化
分别对应下文的print1,2,3函数
#include
#include
using namespace std;
template<class T1,class T2>
class Person {
public:
T1 age;
T2 name;
Person(T1 a, T2 n) {
this->age = a;
this->name = n;
}
void showPerson() {
cout << this->age << this->name << endl;
}
};
void print1(Person<int, string> &T) {
T.showPerson();
}
//参数模板化
template<class T1, class T2>
void print2(Person<T1, T2>& T) {
T.showPerson();
}
//类模板化
template<class T>
void print3(T& p) {
p.showPerson();
}
void test1() {
Person<int, string> p(18, "lyx");
print1(p);
print2(p);
print3(p);
}
int main() {
test1();
}
当类模板碰到继承时
需要注意一下几点:
当子类继承类模板时,子类在声明时,要指定父类T的类型
如果不指定,编译器无法给子类分配内存
如果想要灵活的指定父类中T的类型,则子类也要变成类模板
#include
#include
using namespace std;
template<class T>
class father {
public:
T t;
};
// 两种继承模板父类的方式
class son1 :public father<int> {
public:
int b;
};
template<class T1, class T2>
class son2 :public father<T1>{
public:
T2 c;
};
int main() {
father<int> f1;
f1.t = 2;
son1 t;
t.t = 20;
t.b = 30;
cout << t.t << t.b << endl;
son2<char,int> t2;
t2.t = 'c';
t2.c = 12;
cout << t2.t << t2.c << endl;
}
类模板的分文件编写以及解决
test.h文件 :
#pragma once
#include
#include
using namespace std;
template<class T1, class T2>
class Person {
public:
T1 age;
T2 name;
Person(T1 t, T2 t2);
void showPerson();
};
test.cpp 文件:
#include "test.h"
template<class T1, class T2>
Person<T1, T2>::Person(T1 t, T2 t2) {
this->age = t;
this->name = t2;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << this->age << this->name << endl;
}
main 文件
#include "test.h"
int main() {
Person<string, int> p("tom", 18);
p.showPerson();
}
此时会报错,原因是没有读到test.cpp文件,解决方法有二
第一种是直接在main文件种引用test.cpp文件,第二种是将test.h 和test.cpp 合并写在 test.hpp文件中
使用c++实现array
#include
#include
#pragma once
// 自己的通用数组类
using namespace std;
template<typename T>
class Myarray {
private:
T* pAddress ;// 数组
int m_capacity; // 容量
int m_size; // 大小
public:
// 构造
Myarray(int capacity) {
cout << "构造成功" << endl;
this->m_capacity = capacity;
this->m_size = 0;
this->pAddress = new T[this->m_capacity];
}
//析构
~Myarray() {
cout << "析构成功" << endl;
if (this->pAddress != NULL) {
delete [] this->pAddress;
this->pAddress = NULL;
}
}
//拷贝构造
Myarray(const Myarray & arr) {
this->m_capacity = arr.m_capacity;
this->m_size = arr.m_size;
//this->pAddress = arr.pAddress;
// 解决深拷贝问题
this->pAddress = new T[arr.m_capacity];
//将arr中的数据都拷贝过来
for (int i = 0; i < this->m_size; i++) {
this->pAddress[i] = arr.pAddress[i];
}
cout << "拷贝成功!" << endl;
}
Myarray& operator= (const Myarray & arr){
//先判断原来堆区是否有数据 如果有就释放
if (this->pAddress != NULL) {
delete [] this->pAddress;
}
this->m_capacity = arr.m_capacity;
this->m_size = arr.m_size;
this->pAddress = new T[arr.m_capacity];
for (int i = 0; i < this->m_size; i++) {
this->pAddress[i] = arr.pAddress[i];
}
cout << "等式成功" << endl;
return *this;
}
//尾插法
void push_back(const T & val) {
if (this->m_capacity == this->m_size) {
cout << "数组已满" << endl;
return;
}
this->pAddress[this->m_size] = val;
this->m_size++;
}
//尾删法
void pop_back() {
// 让用户访问不到元素,即为尾删
if (this->m_size == 0)
{
return;
}
this->m_size--;
}
//通过下标访问元素 arr[0] = 100
// 注意: 如果函数调用想要作为T的左值存在,还要返回T的引用
T& operator[](int index){
return this->pAddress[index];
}
// 返回数组容量
int getCapacity() {
return this->m_capacity;
}
// 返回数组大小
int getSize() {
return this->m_size;
}
};
#include "实现通用数组.hpp"
void printIntArray(Myarray<int>&A) {
for (int i = 0; i < A.getSize(); i++) {
cout << A[i] << endl;
}
}
void test01() {
Myarray <int>arr1(5);
// Myarray arr2(arr1);
// Myarray arr3(5);
// arr3 = arr1;
for (int i = 0; i < 5; i++)
{
arr1.push_back(i);
}
cout << arr1[0] << endl;
printIntArray(arr1);
Myarray <int> arr2(arr1);
arr2.pop_back();
cout << arr2.getSize() << endl;
printIntArray(arr2);
}
class Person {
public:
int age;
string name;
Person(int age, string name) {
this->age = age;
this->name = name;
}
Person() {
}
};
void printPerson(Myarray<Person> & arr) {
for (int i = 0; i < arr.getSize(); i++) {
cout << arr[i].age << arr[i].name << endl;
}
}
void test02() {
Person p1(20, "lyx");
Person p2(22, "lyx");
Myarray<Person> arr2(100);
arr2.push_back(p1);
arr2.push_back(p2);
printPerson(arr2);
};
int main() {
test02();
}