c++另一种编程思想称为泛型编程,主要利用的技术就是模板。
c++提供两个模板机制,函数模板和类模板
函数模板语法
函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代表
语法:
template
函数声明或定义
template->声明创建模板
typename->表明其后面的符号是一种数据类型,可以用class代替
T ->通用的数据类型,名称可以替换,通常为大写字母
函数模板注意事项
自动类型推导,必须推导出一致的数据类型T,才可以使用
模板必须要确定出T的数据类型,才可以使用
#include#include using namespace std; template void myswap(T& a, T& b) { T temp = a; a = b; b = temp; } template 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 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 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 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 p = Person (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 class myclass { //不会报错 public: T t; void myfun1() { t.func1(); } void myfun2() { t.func2(); } }; int main() { myclass p; p.myfun1(); //p.myfun2(); //调用时才报错 }
类模板对象做参数调用,有三种方法
第一种:指定传入类型
第二种:参数模板化
第三种: 整个类模板化
分别对应下文的print1,2,3函数
#include#include using namespace std; template 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 &T) { T.showPerson(); } //参数模板化 template void print2(Person & T) { T.showPerson(); } //类模板化 template void print3(T& p) { p.showPerson(); } void test1() { Person p(18, "lyx"); print1(p); print2(p); print3(p); } int main() { test1(); }
当类模板碰到继承时
需要注意一下几点:
当子类继承类模板时,子类在声明时,要指定父类T的类型
如果不指定,编译器无法给子类分配内存
如果想要灵活的指定父类中T的类型,则子类也要变成类模板
#include#include using namespace std; template class father { public: T t; }; // 两种继承模板父类的方式 class son1 :public father { public: int b; }; template class son2 :public father { public: T2 c; }; int main() { father f1; f1.t = 2; son1 t; t.t = 20; t.b = 30; cout << t.t << t.b << endl; son2 t2; t2.t = 'c'; t2.c = 12; cout << t2.t << t2.c << endl; }
类模板成员函数的类外实现
类模板的分文件编写以及解决
test.h文件 :
#pragma once #include#include using namespace std; template class Person { public: T1 age; T2 name; Person(T1 t, T2 t2); void showPerson(); };
test.cpp 文件:
#include "test.h" templatePerson ::Person(T1 t, T2 t2) { this->age = t; this->name = t2; } template void Person ::showPerson() { cout << this->age << this->name << endl; }
main 文件
#include "test.h"
int main() {
Person 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 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&A) { for (int i = 0; i < A.getSize(); i++) { cout << A[i] << endl; } } void test01() { Myarray 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 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 & 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 arr2(100); arr2.push_back(p1); arr2.push_back(p2); printPerson(arr2); }; int main() { test02(); }



