本专栏记录C++学习过程包括C++基础以及数据结构和算法,其中第一部分计划时间一个月,主要跟着黑马视频教程,学习路线如下,不定时更新,欢迎关注。
当前章节处于:
---------第1阶段-C++基础入门
---------第2阶段实战-通讯录管理系统,
---------第3阶段-C++核心编程,
---------第4阶段实战-基于多态的企业职工系统
=====>第5阶段-C++提高编程
---------第6阶段实战-基于STL泛化编程的演讲比赛
---------第7阶段-C++实战项目机房预约管理系统
模板就是建立通用的模具,大大提高复用性。模板不可以直接使用,只是一个框架,并且模板的通用并不是万能的。使用模板的目的是提高复用性,将类型参数化。
template
函数声明或定义
#include <iostream>
using namespace std;
// 定义一个交换的函数模板
template <typename T>
void Myswap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
void test() {
int a = 10;
int b = 20;
char c = 'c';
char d = 'd';
// 1. 自动推导型
Myswap(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
// 2. 显示推导型
Myswap<char>(c, d);
cout << "c=" << c << endl;
cout << "d=" << d << endl;
}
int main() {
test();
system("pause");
return 0;
}
a=20
b=10
c=d
d=c
请按任意键继续. . .
#include <iostream>
using namespace std;
// 定义模板
template <class T>
void func() {
cout << "调用函数模板" << endl;
}
void test() {
// func(); // 错误
func<int>(); // 正确
}
int main() {
test();
system("pause");
return 0;
}
调用函数模板
请按任意键继续. . .
实战案例
#include <iostream>
using namespace std;
// 排序函数模板
template<class T>
void func(T arr[],int length) {
// 选择排序
for (int i = 0; i < length; i++) {
int max = i; // 认为当前的是最大值
for (int j = i+1; j < length; j++) {
if (arr[max] < arr[j]) {
max = j;
}
}
// 调换两个值
T temp = arr[i];
arr[i] = arr[max];
arr[max] = temp;
}
}
// 打印函数模板
template <class T>
void printArr(T arr[],int length) {
for (int i = 0; i < length; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
// 测试整型
void test01() {
int arr[10] = { 1,4,3,2,6,7,5,9,8,0 };
func(arr, 10);
printArr(arr, 10);
}
void test02() {
char arr[] = "acbdfeg";
int length = sizeof(arr) / sizeof(arr[0]);
func(arr,length);
printArr(arr, length);
}
int main() {
test01();
test02();
system("pause");
return 0;
}
9 8 7 6 5 4 3 2 1 0
g f e d c b a
请按任意键继续. . .
#include <iostream>
using namespace std;
// 定义一个函数模板
template<class T>
T func1(T a, T b) {
cout << "调用的是函数模板!" << endl;
return a + b;
}
// 定义一个普通函数
int func2(int a,int b) {
cout << "调用的是普通函数!" << endl;
return a + b;
}
// 测试案例
void test() {
int a = 10;
int b = 20;
char c = 'c';
cout << func1(a, b) << endl;
cout << func2(a, c) << endl;
//cout << func1(a, c) << endl;// 报错
cout << func1<int>(a, c) << endl;// 不报错
}
int main() {
test();
system("pause");
return 0;
}
调用的是函数模板!
30
调用的是普通函数!
109
调用的是函数模板!
109
请按任意键继续. . .
下面通过具体代码进行逐个讲解
#include <iostream>
using namespace std;
// 定义普通函数
void MyPrint(int a,int b) {
cout << "调用的是普通函数" << endl;
}
// 定义模板函数
template<class T>
void MyPrint(T a, T b) {
cout << "调用的是模板函数" << endl;
}
// 重载模板函数
template<typename T>
void MyPrint(T a, T b, T c)
{
cout << "调用重载的模板函数" << endl;
}
int main() {
// 1. 如果函数模板和普通函数都可以实现,优先调用普通函数
int a = 10;
int b = 20;
MyPrint(a, b);
// 2.可以通过空模板参数列表来强制调用函数模板
MyPrint<>(a, b);
// 3.函数模板也可以发生重载
MyPrint(a, b, 30);
// 4.如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
MyPrint(c1, c2);
system("pause");
return 0;
}
调用的是普通函数
调用的是模板函数
调用重载的模板函数
调用的是模板函数
请按任意键继续. . .
总结:如果提供了函数模板,最好就不要再提供对应的普通函数,否则容易出现二义性。
如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常进行比较运算,C++为了解决这种问题,提供模板的重载,为这些特定类型提供具体化模板。
#include <iostream>
using namespace std;
class Person {
public:
Person(string name,int age) {
m_Name = name;
m_Age = age;
}
string m_Name;
int m_Age;
};
// 普通函数模板
template<class T>
bool myCompare(T& a, T& b) {
return a == b;
}
// 具体化 优先于常规模板
template<> bool myCompare(Person& p1, Person& p2) {
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
{
return true;
}
else
{
return false;
}
}
void test() {
Person p1("Tom", 10);
Person p2("Tom", 10);
cout << myCompare(p1, p2) << endl;
}
int main() {
test();
system("pause");
return 0;
}
1
请按任意键继续. . .
利用具体化的模板,可以解决自定义类型的通用化,学习模板并不是为了写模板,而是再STL中能够运用系统提供的模板。
类似于函数模板,类有也对应的类模板,类模板的作用是建立一个通用类,类中的成员,数据类型可以不具体制定,用一个虚拟的类型来代表,语法为
template<typename T>
类
#include <iostream>
using namespace std;
// 定义一个交换的函数模板
template <typename T>
void Myswap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
void test() {
int a = 10;
int b = 20;
char c = 'c';
char d = 'd';
// 1. 自动推导型
Myswap(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
// 2. 显示推导型
Myswap<char>(c, d);
cout << "c=" << c << endl;
cout << "d=" << d << endl;
}
int main() {
test();
system("pause");
return 0;
}
姓名:孙悟空 年龄:999
姓名:猪八戒 年龄:888
请按任意键继续. . .
类模板中成员函数和普通类中成员函数创建时机是有区别的:
#include <iostream>
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout << "Person1 show" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "Person2 show" << endl;
}
};
// 定义类模板
template<class T>
class MyClass
{
public:
T obj;
//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
void fun1() { obj.showPerson1(); }
void fun2() { obj.showPerson2(); }
};
void test01()
{
MyClass<Person1> m;
m.fun1();
//m.fun2();//编译会出错,说明函数调用才会去创建成员函数
}
int main() {
test01();
system("pause");
return 0;
}
Person1 show
请按任意键继续. . .
类模板实例化出的对象,向函数传参的方式
一共有三种传入方式:
下面用代码进行讲解:
#include <iostream>
using namespace std;
// 创建一个类模板
template<class NameType, class AgeType = int> // 默认参数为int
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
//1、指定传入的类型,最常用的一种方式
void printPerson1(Person<string, int>& p)
{
p.showPerson();
}
void test01()
{
Person <string, int >p("孙悟空", 100);
printPerson1(p);
}
//2、参数模板化
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
p.showPerson();
cout << "T1的类型为: " << typeid(T1).name() << endl;
cout << "T2的类型为: " << typeid(T2).name() << endl;
}
void test02()
{
Person <string, int >p("猪八戒", 90);
printPerson2(p);
}
//3、整个类模板化
template<class T>
void printPerson3(T& p)
{
cout << "T的类型为: " << typeid(T).name() << endl;
p.showPerson();
}
void test03()
{
Person <string, int >p("唐僧", 30);
printPerson3(p);
}
int main() {
test01();
test02();
test03();
system("pause");
return 0;
}
int main() {
system("pause");
return 0;
}
name: 孙悟空 age: 100
name: 猪八戒 age: 90
T1的类型为: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
T2的类型为: int
T的类型为: class Person<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int>
name: 唐僧 age: 30
请按任意键继续. . .
当类模板碰到继承时,需要注意一下几点:
#include <iostream>
// 父类类模板
using namespace std;
template<class T>
class Base
{
T m;
};
// 子类继承父类
// class Son :public Base {}; // 错误,必须要确定父类中的T
class Son :public Base<int> {
};
void test01()
{
Son c;// 实例化对象
}
template<class T2>
class Son2 :public Base<T2>
{
public:
Son2()
{
cout << typeid(T2).name() << endl;
}
};
void test02()
{
Son2<char> child1;
}
int main() {
test01();
test02();
system("pause");
return 0;
}
char
请按任意键继续. . .
#include <iostream>
using namespace std;
template<class NameType, class AgeType = int> // 默认参数为int
class Person
{
public:
Person(NameType name, AgeType age);
void showPerson();
public:
NameType mName;
AgeType mAge;
};
// 构造函数 类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
// 成员函数类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
int main() {
Person<string, int> p("张三", 13);
p.showPerson();
system("pause");
return 0;
}
name: 张三 age: 13
请按任意键继续. . .
由于类模板中成员函数创建时机是在调用阶段,导致分文件编写时连接不到,有两种解决方案:
Person.hpp
#pragma once
#include <iostream>
using namespace std;
template<class NameType, class AgeType = int> // 默认参数为int
class Person
{
public:
Person(NameType name, AgeType age);
void showPerson();
public:
NameType mName;
AgeType mAge;
};
// 构造函数 类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
// 成员函数类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
类模板分文件编写.cpp
#include <iostream>
using namespace std;
#include "Person.hpp"
int main() {
Person<string, int> p("张三", 13);
p.showPerson();
system("pause");
return 0;
}
name: 张三 age: 13
请按任意键继续. . .
#include <iostream>
using namespace std;
template <class T1,class T2>
class Person;
// 2. 全局函数类外实现
template <class T1, class T2>
void showPerson(Person<T1, T2> p) {
cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
template <class T1, class T2>
class Person {
// 1. 全局函数类内实现
// 加上friend变为全局函数,如果不加的话就是成员函数
//friend void showPerson(Person<T1,T2> p) {
// cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
//}
// 如果全局函数是类外实现,需要让编译器提前知道这个函数存在
friend void showPerson<>(Person<T1, T2> p);
public:
Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
int main() {
Person<string, int> p("Tom", 12);
showPerson(p);
system("pause");
return 0;
}
姓名:Tom 年龄:12
请按任意键继续. . .
实战案例
实现一个通用的数组类
main.cpp
#include <iostream>
using namespace std;
#include "MyArray.hpp"
class Person {
public:
Person() {
}
Person(string name, int age) {
m_name = name;
m_age = age;
}
string getName() {
return m_name;
}
int getAge() {
return m_age;
}
private:
string m_name;
int m_age=0;
};
void showPerson(Myarray<Person> array,int num) {
for (int i = 0; i < num; i++) {
cout << "姓名:" << array[i].getName() << " 年龄:"<<array[i].getAge()<<endl;
}
}
void showInt(Myarray<int> arr, int num) {
for (int i = 0; i < num; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
void test() {
Myarray<int> arr(10);
for (int i = 0; i < 10; i++) {
arr.insert_Back(i);
}
showInt(arr, 10);
//Myarray<int> arr2(arr);
//Myarray<int> arr3(20);
//arr3 = arr;
//cout <<"arr3的容量为"<< arr3.get_Mcap() << endl;
Myarray<Person> PersonArr(2);
Person p1("张三", 12);
Person p2("李四", 21);
PersonArr.insert_Back(p1);
PersonArr.insert_Back(p2);
showPerson(PersonArr,2);
}
int main() {
test();
system("pause");
return 0;
}
MyArray.hpp
#pragma once
#include <iostream>
using namespace std;
template <class T>
class Myarray {
public:
// 有参构造
Myarray(int cap) {
cout << "调用有参构造函数" << endl;
//cout << "hello" << endl;
this->m_cap = cap;
this->pArray = new T[this->m_cap]; // 在堆区开辟
this->size = 0;
}
// 拷贝构造
Myarray(Myarray& arr) {
//cout << "调用拷贝构造函数" << endl;
this->size = arr.size;
this->m_cap = arr.m_cap;
this->pArray = new T[arr.m_cap];
// 将arr中的数据拿过来
for (int i = 0; i < arr.size; i++) {
this->pArray[i] = arr.pArray[i];
}
}
// 析构函数
~Myarray() {
if (this->pArray!= NULL) {
cout << "调用析构函数" << endl;
this->m_cap = 0;
this->size = 0;
// pArray是个数组 删除时要注意格式
delete[] pArray;
pArray = NULL;
//cout << "删除完成" << endl;
}
}
// 重载=操作运算符
Myarray& operator=(const Myarray& arr) {
//cout << "调用operator=函数" << endl;
// 先判断原来栈区是否有数据
if (this->pArray != NULL) {
delete[] pArray;
pArray = NULL;
this->m_cap = 0;
this->size = 0;
}
this->m_cap = arr.m_cap;
this->size = arr.size;
this->pArray = new T[arr.m_cap];
return *this;
}
// 重载[]操作运算符
T& operator[](int index) {
return pArray[index];
}
// 获取数组容量
int get_Mcap() {
return this->m_cap;
}
// 获取数组大小
int get_Size() {
return this->size;
}
// 获取
// 尾插法
void insert_Back(T value) {
// 判断还有无空间
if (this->size == this->m_cap) {
cout << "达到插入上限!" << endl;
return ;
}
this->pArray[this->size] = value;
this->size++;
//return *this;
}
// 尾删法
void pop_Back() {
// 判断是否为0
if (this->size == 0) {
cout << "已达删除上限!" << endl;
return;
}
this->size--;
}
// 通过下标方式访问元素
T& get_index(int index) {
if (index<0 || index>this.size) {
cout << "索引有无!" << endl;
return;
}
return this->pArray[index];
}
private:
T* pArray; // 头指针
int m_cap; // 容量
int size; // 大小
};
调用有参构造函数
0 1 2 3 4 5 6 7 8 9
调用析构函数
调用有参构造函数
姓名:张三 年龄:12
姓名:李四 年龄:21
调用析构函数
调用析构函数
调用析构函数
请按任意键继续. . .