C++模板
·
1.模板的概念
模板就是建立通用的模具,大大提高复用性。
模板的特性:
- 模板不可以直接使用,它只是一个框架。
- 模板的通用并不是万能的。
C++提供两种模板机制:函数模板和类模板。
2.函数模板
2.1 函数模板语法
函数模板的作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
目的:提高复用性,将类型参数化。
语法: template<typename T>
函数声明或定义
template ----- 声明创建模板
typename ----- 表明其后面的符号是一种数据结构,可以用class代替
T ----- 通用的数据类型,名称可以替换,通常为大写字母
#include <iostream>
using namespace std;
// 交换整型数据
void swapINT(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
// 交换浮点型数据
void swapDOUBLE(double& a, double& b)
{
double temp = a;
a = b;
b = temp;
}
// 函数模板
template<typename T>
void mySWAP(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test()
{
int a = 1;
int b = 2;
//swapINT(a, b);
// 两种方式使用函数模板
// 1.自动类型推导
//mySWAP(a, b);
// 2.显示指定类型
mySWAP<int>(a, b);
cout << "a= " << a << endl;
cout << "b= " << b << endl;
/*double c = 1.1;
double d = 2.2;
swapDOUBLE(c, d);
cout << "c= " << c << endl;
cout << "d= " << d << endl;*/
}
int main()
{
test();
return 0;
}
2.2 函数模板注意事项
- 自动类型推导,必须推导出一致的数据类型T,才可以使用
- 模板必须要确定T的数据类型才可以使用
#include <iostream>
using namespace std;
// 函数模板
template<class T>
void mySWAP(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test()
{
int a = 1;
int b = 2;
char c = 'c';
//mySWAP(a, c); //错误,推导不出一致的T类型
cout << "a= " << a << endl;
cout << "b= " << b << endl;
}
// 模板必须要确定出T的数据类型
template<class T>
void func()
{
cout << "func的调用" << endl;
}
void test2()
{
func<int>();
}
int main()
{
test();
test2();
return 0;
}
例1:利用函数模板封装一个排序的函数,排序规则为从大到小,排序算法为选择排序。
#include <iostream>
using namespace std;
template<class T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template<class T>
void sort(T a[],int len)
{
for (int i = 0; i < len; i++)
{
int max = i;
for (int j = i + 1; j < len; j++)
{
if (a[max] < a[j])
max = j;
}
if (max != i)
{
mySwap(a[max], a[i]);
}
}
}
template<class T>
void print(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << ' ';
}
}
void test()
{
char arr[] = "ahkgs";
int len = sizeof(arr) / sizeof(char);
sort(arr, len);
print(arr, len);
}
void test2()
{
int arr[] = { 2,4,1,6,3,7 };
int len = sizeof(arr) / sizeof(int);
sort(arr, len);
print(arr, len);
}
int main()
{
//test();
test2();
return 0;
}
2.3 普通函数和函数模板的区别
- 普通函数调用的时候可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
#include <iostream>
using namespace std;
// 普通函数
int add(int a, int b)
{
return a + b;
}
// 模板函数
template<class T>
T myAdd(T a, T b)
{
return a + b;
}
void test()
{
int a = 1;
int b = 2;
char c = 'c';
//cout << add(a, b) << endl;
//cout << add(a, c) << endl; //c - 99 此时为隐式类型转换
cout << myAdd(a, b) << endl;
// 自动类型推导 不会发生隐式转换
//cout << myAdd(a, c) << endl; //此时报错,不会发生隐式转换
// 显示指定类型 会发生隐式转换
cout << myAdd<int>(a, c) << endl;
}
int main()
{
test();
return 0;
}
2.4 普通函数与函数模板的调用规则
- 如果函数模板和普通函数都可以实现,优先调用普通函数。
- 可以通过空模板参数列表来强制调用函数模板。
- 函数模板也可以发生重载。
- 如果函数模板可以产生更好的匹配,优先调用函数模板。
#include <iostream>
using namespace std;
void print(int a, int b)
{
cout << "调用普通函数" << endl;
}
template<class T>
void print(T a, T b)
{
cout << "调用函数模板" << endl;
}
template<class T>
void print(T a, T b,T c)
{
cout << "调用重载的函数模板" << endl;
}
void test()
{
int a = 1;
int b = 2;
//print(a, b);
//通过空模板参数列表强制调用函数模板
//print<>(a, b);
//print(a, b, 10);
// 当函数模板可以更好的匹配,会调用函数模板
char c1 = 'a';
char c2 = 'b';
print(c1, c2);
}
int main()
{
test();
return 0;
}
当我们提供了函数模板时,最好不要再提供普通函数了,容易出现二义性。
2.5 模板的局限性
局限性:模板的通用性不是万能的。
为了解决这个问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
#include <iostream>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
template<class T>
bool compare(T& a, T& b)
{
if (a == b)
return true;
else
return false;
}
// 利用具体化Person的版本实现代码,具体化优先调用
template<>bool compare(Person& p1, Person& p2)
{
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
return true;
else
return false;
}
void test()
{
int a = 1;
int b = 2;
bool ret = compare(a, b);
if (ret)
{
cout << "a==b" << endl;
}
else
cout << "a!=b" << endl;
}
void test2()
{
Person p1("张三", 18);
Person p2("张三", 19);
bool ret = compare(p1, p2);
if (ret)
cout << "p1==p2" << endl;
else
cout << "p1!=p2" << endl;
}
int main()
{
//test();
test2();
return 0;
}
3. 类模板
3.1 类模板基本语法
作用:建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表。
语法: template<typename T>
类
#include <iostream>
using namespace std;
template<class NameType,class AgeType>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Age = age;
this->m_Name = name;
}
void show()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test()
{
Person<string, int>p1("张三", 18);
p1.show();
}
int main()
{
test();
return 0;
}
3.2 类模板和函数模板的区别
- 类模板没有自动类型推导的使用方法
- 类模板在函数模板列表中可以有默认参数
#include <iostream>
using namespace std;
template<class NameType,class AgeType=int> // 只有类模板可以有默认参数
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Age = age;
this->m_Name = name;
}
void show()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test()
{
//Person p("张三", 18); //有些编译器可以自动类型推导,有些编译器不行
Person<string,int> p("张三", 18); //最好使用显示指定类型
p.show();
}
void test2()
{
Person<string>p1("李四", 19);
p1.show();
}
int main()
{
test();
test2();
return 0;
}
3.3 类模板中成员函数创建时机
类模板中的成员函数在调用时才创建。
#include <iostream>
using namespace std;
class Person1
{
public:
void show1()
{
cout << "Person1 show" << endl;
}
};
class Person2
{
public:
void show2()
{
cout << "Person2 show" << endl;
}
};
template<class T>
class Class
{
public:
T obj;
void func1()
{
obj.show1();
}
void func2()
{
obj.show2();
}
};
void test1()
{
Class<Person1>m;
m.func1();
//m.func2(); 编译错误,说明函数调用才会去创建成员函数
}
int main()
{
test1();
return 0;
}
3.4 类模板对象做函数参数
三种传入方式:
- 指定传入的类型 --- 直接显示对象的数据类型 (使用比较广泛)
- 参数模板化 --- 将对象中的参数变为模板进行传递
- 整个类模板化 --- 将这个对象类型模板化进行传递
#include <iostream>
using namespace std;
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
void show()
{
cout << "name = " << m_Name << " age = " << m_Age << endl;
}
T1 m_Name;
T2 m_Age;
};
//1. 指定传入类型
void print1(Person<string, int>& p)
{
p.show();
}
void test1()
{
Person<string, int>p("张三", 18);
print1(p);
}
//2. 参数模板化
template<class T1,class T2>
void print2(Person<T1, T2>& p)
{
p.show();
cout << "T1的类型:" << typeid(T1).name() << endl;
cout << "T2的类型:" << typeid(T2).name() << endl;
}
void test2()
{
Person<string, int>p("李四", 19);
print2(p);
}
//3. 整个类模板化
template<class T>
void print3(T& p)
{
p.show();
cout << "T的类型:" << typeid(T).name() << endl;
}
void test3()
{
Person<string, int>p("王五", 20);
print3(p);
}
int main()
{
//test1();
//test2();
test3();
return 0;
}
3.5 类模板与继承
注意:
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型。
- 如果不指定,编译器无法给子类分配内存。
- 如果想灵活指定出父类中T的类型,子类也需变成类模板。
#include <iostream>
using namespace std;
template<class T>
class Base
{
public:
T m;
};
//class Son :public Base // 错误,必须要知道父类中T的类型
class Son:public Base<int>
{
};
void test()
{
Son s1;
}
// 想要灵活指定父类中T的类型,子类也需要变为类模板
template<class T,class T2>
class Son2 :public Base<T>
{
T2 obj;
};
void test2()
{
Son2<int, char>s;
}
int main()
{
return 0;
}
3.6 类模板成员函数类外实现
#include <iostream>
using namespace std;
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age);
/*{
this->m_Age = age;
this->m_Name = name;
}*/
void show();
/*{
cout << "name = " << m_Name << " age = " << m_Age << endl;
}*/
T1 m_Name;
T2 m_Age;
};
// 构造函数类外实现
template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
// 成员函数类外实现
template<class T1, class T2>
void Person<T1, T2>::show()
{
cout << "name = " << m_Name << " age = " << m_Age << endl;
}
void test()
{
Person<string, int>p("Tom", 18);
p.show();
}
int main()
{
test();
return 0;
}
3.7 类模板分文件编写
类模板中成员函数创建时机是在调试阶段,导致分文件编写时链接不到。
此时有两种解决方法:
1. 直接包含.cpp源文件。
2. 将声明和实现写在同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,可以更改。
方法一:
// 头文件Person.h
#pragma once
#include <iostream>
using namespace std;
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
void show();
T1 m_Name;
T2 m_Age;
};
// 源文件 Person.cpp
#include "Person.h"
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
template<class T1, class T2>
void Person<T1, T2>::show()
{
cout << "name = " << m_Name << " age = " << m_Age << endl;
}
#include <iostream>
using namespace std;
// 解决方法一 : 将 .h 改为 .cpp
#include "Person.cpp"
void test()
{
Person<string, int> p("Tom", 18); //此时编译错误
p.show();
}
int main()
{
test();
return 0;
}
方法二:
// 类模板Person.hpp
#pragma once
#include <iostream>
using namespace std;
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
void show();
T1 m_Name;
T2 m_Age;
};
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
template<class T1, class T2>
void Person<T1, T2>::show()
{
cout << "name = " << m_Name << " age = " << m_Age << endl;
}
#include <iostream>
using namespace std;
#include "Person.hpp"
void test()
{
Person<string, int> p("Tom", 18); //此时编译错误
p.show();
}
int main()
{
test();
return 0;
}
3.8 类模板与友元
全局函数类内实现 --- 直接在类内声明友元即可
全局函数类外实现 --- 需要提前让编译器知道全局函数的存在
#include <iostream>
using namespace std;
// 提前让编译器知道Peraon类的存在
template<class T1, class T2>
class Person;
// 类外实现
template<class T1, class T2>
void print2(Person<T1, T2> p)
{
cout << "类外 name = " << p.m_Name << " age = " << p.m_Age << endl;
}
template<class T1,class T2>
class Person
{
// 全局函数类内实现
friend void print(Person<T1,T2> p)
{
cout << "name = " << p.m_Name << " age = " << p.m_Age << endl;
}
// 全局函数类外实现
// 如果全局函数是类外实现的,需要让编译器提前知道这个函数的存在
friend void print2<>(Person<T1, T2> p);
public:
Person(T1 name, T2 age)
{
this->m_Age = age;
this->m_Name = name;
}
private:
T1 m_Name;
T2 m_Age;
};
void test()
{
Person<string, int> p("Tom", 18); //此时编译错误
print(p);
}
void test2()
{
Person<string, int> p("Amy", 18);
print2(p);
}
int main()
{
//test();
test2();
return 0;
}
3.9 案例
实现一个通用的数组类,要求:
- 可以对内置数据类型以及自定义类型的数据进行存储
- 将数组中的数据存储到堆区
- 构造函数中可以传入数组的容量
- 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
- 提供尾插法和尾删法对数组中的数据进行增加和删除
- 可以通过下标的方法访问数组中的元素
- 可以获取数组中当前元素个数和数组的容量
// mwArray.hpp
#pragma once
#include <iostream>
using namespace std;
template<class T>
class myArray
{
public:
// 有参函数
myArray(int capacity)
{
this->m_capacity = capacity;
this->m_size = 0;
pAddress = new T[this->m_capacity];
//cout << "myArray有参函数调用" << endl;
}
// 拷贝函数
myArray(const myArray& arr)
{
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 << "myArray的拷贝函数调用" << endl;
}
// operator=
myArray& operator=(const myArray& arr)
{
//cout << "myArray的 operator= 函数调用" << endl;
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
this->m_capacity = 0;
this->m_size = 0;
}
this->m_capacity = arr.m_capacity;
this->m_size = arr.m_size;
this->pAddress = new T[this->m_capacity];
for (int i = 0; i < this->m_size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
// 尾插法
void push_back(const T& val)
{
// 判断容量的大小
if (this->m_capacity == this->m_size)
{
return;
}
this->pAddress[this->m_size] = val;
this->m_size++;
}
// 尾删法
void pop_back()
{
if (this->m_size == 0)
{
return;
}
this->m_size--;
}
// 通过下标的方式来访问
T& operator[](int index)
{
return this->pAddress[index];
}
// 返回数组容量
int getcapacity()
{
return this->m_capacity;
}
// 返回数组大小
int getsize()
{
return this->m_size;
}
// 析构函数
~myArray()
{
//cout << "myArray的析构函数调用" << endl;
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
}
}
private:
int m_capacity;
int m_size;
T* pAddress;
};
#include <iostream>
using namespace std;
#include "myArray.hpp"
void print(myArray<int>& arr)
{
for (int i = 0; i < arr.getsize(); i++)
{
cout << arr[i] << endl;
}
}
void test()
{
myArray<int>arr1(5);
for (int i = 0; i < 5; i++)
{
arr1.push_back(i);
}
print(arr1);
cout << "arr1的容量:" << arr1.getcapacity() << endl;
cout << "arr1的大小:" << arr1.getsize() << endl;
myArray<int>arr2(arr1);
//尾删
arr2.pop_back();
cout << "arr2的容量:" << arr2.getcapacity() << endl;
cout << "arr2的大小:" << arr2.getsize() << endl;
/*myArray<int>arr2(arr1);
myArray<int>arr3(100);
arr3 = arr1;*/
}
// 自定义类型
class Person
{
public:
Person() {};
Person(string name, int age)
{
this->m_Age = age;
this->m_Name = name;
}
string m_Name;
int m_Age;
};
void printPerson(myArray<Person>& arr)
{
for (int i = 0; i < arr.getsize(); i++)
{
cout << "name = " << arr[i].m_Name << " age = " << arr[i].m_Age << endl;
}
}
void test2()
{
myArray<Person> arr(10);
Person p1("张三", 18);
Person p2("李四", 19);
Person p3("王五", 20);
Person p4("Tom", 21);
Person p5("Amy", 22);
//尾插
arr.push_back(p1);
arr.push_back(p2);
arr.push_back(p3);
arr.push_back(p4);
arr.push_back(p5);
printPerson(arr);
cout << "arr的容量:" << arr.getcapacity() << endl;
cout << "arr的大小:" << arr.getsize() << endl;
}
int main()
{
//test();
test2();
return 0;
}
更多推荐
所有评论(0)