文章目录
1.2、类模板
1.2.1、类模板语法
#include <iostream>
using namespace std;
//类模板
template<class NameType , class AgeType>
class Person
{
public:
Person(NameType name , AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void showPerson()
{
cout << "name : " << this->m_Name << " age: " << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test01()
{
Person<string , int> p1("小红" , 33);
p1.showPerson();
}
int main() {
test01();
return 0;
}
1.2.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 << "name : " << this->m_Name << " age :" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
//1、类模板没有自动类型推导使用方式
void test01()
{
//Person p ("小红",111); //错误,无法自动类型推导
//正确
Person<string,int> p("小红",111);
p.show();
}
//2、类模板在模板参数列表中可以有默认参数
void test02()
{
Person<string> p ("猪",22);
p.show();
}
int main() {
// test01();
test02();
return 0;
}
1.2.3、类模板中成员函数创建时机
- 类模板中成员函数在调用时才去创建
类模板中的成员函数并不是一开始就创建的,在调用时才创建
#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 func1()
{
obj.ShowPerson1();
}
void func2()
{
obj.ShowPerson2();
}
};
void test()
{
MyClass<Person2> m;
// m.func1();
m.func2();
}
int main() {
test();
return 0;
}
1.2.4、类模板对象做函数参数
三种传入方式:
- 指定传入的类型 (最常用)
- 参数模板化
- 整个类模板化
#include <iostream>
using namespace std;
//类模板对象做函数参数
template<class T1 , class T2>
class Person
{
public:
Person(T1 name , T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
void show()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
T1 m_Name;
T2 m_Age;
};
//1、指定传入类型
void printPerson1(Person<string,int> &p)
{
p.show();
}
void test01()
{
Person<string,int> p("小猪",22);
printPerson1(p);
}
//2、参数模板化
template<class T1 , class T2>
void printPerson2(Person<T1 , T2> &p)
{
p.show();
cout << "T1 的类型为:" << typeid(T1).name() << endl;
cout << "T2 的类型为:" << typeid(T2).name() << endl;
}
void test02()
{
Person<string,int> p("柚子",22);
printPerson2(p);
}
//3、整个类模板化
template<class T>
void printPerson3(T &p)
{
p.show();
cout << "T 的类型为:" << typeid(p).name() << endl;
}
void test03()
{
Person<string,int> p("柚子",22);
printPerson3(p);
}
int main() {
//test01();
//test02();
test03();
return 0;
}
1.2.5、类模板与继承
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中T类型,子类也需要变为类模板
#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 s1;
}
//如果想灵活指定父类中T类型,子类也需要变类模板
template<class T1 , class T2>
class Son2 : public Base<T2>
{
public:
Son2()
{
cout << "T1 的类型为 :" << typeid(T1).name() << endl;
cout << "T2 的类型为 :" << typeid(T2).name() << endl;
}
T1 obj;
};
void test02()
{
Son2<int ,char>S2;
}
int main() {
// test01();
test02();
return 0;
}
1.2.6、类模板成员函数类外实现
#include <iostream>
using namespace std;
//类模板成员函数类外实现
template<class T1 , class T2>
class Person
{
public:
Person(T1 name , T2 age);
/* {
this->m_Name = name;
this->m_Age = age;
}*/
void showPerson();
/* {
cout << "姓名 :" << this->m_Name << " 年龄:" << this->m_Age << endl;
}*/
T1 m_Name;
T2 m_Age;
};
//构造函数类外实现
template<class T1,class T2>
Person<T1,T2>::Person(T1 name , T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1,class T2>
void Person<T1,T2>::showPerson()
{
cout << "姓名 :" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
void test01()
{
Person<string,int> p("小猪",22);
p.showPerson();
}
int main() {
test01();
return 0;
}
1.2.7、类模板分文件编写
问题:
- 类模板中成员函数创建时机是在调用阶段,导致文件编写时链接不到
解决:
-
方法一:直接包含.cpp源文件
-
方法二:将声明和实现写到同一个文件中,并更改后缀为 .hpp是约定的名称,并不是强制
person.hpp中代码
#include <iostream>
using namespace std;
template<class T1 , class T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerons();
T1 m_Name;
T2 m_Age;
};
template<class T1 , class T2>
Person<T1,T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1 , class T2>
void Person<T1,T2>::showPerons()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
person.h中代码
//
// Created by Administrator on 2021/8/6.
//
#ifndef _03_STL_PERSON_H
#define _03_STL_PERSON_H
#include <iostream>
using namespace std;
template<class T1 , class T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerons();
T1 m_Name;
T2 m_Age;
};
#endif //_03_STL_PERSON_H
person.cpp中代码
//
// Created by Administrator on 2021/8/6.
//
#include "person.h"
template<class T1 , class T2>
Person<T1,T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1 , class T2>
void Person<T1,T2>::showPerons()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
测试
#include <iostream>
using namespace std;
//第一种解决方法,直接包含 源文件
//#include "person.cpp"
//第二种解决方法,将.h和 .cpp的内容放到一起,将后缀名改为 .hpp (常用)
#include "Person.hpp"
//类模板分文件编写问题以及解决
/*
template<class T1 , class T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerons();
T1 m_Name;
T2 m_Age;
};
*/
/*
template<class T1 , class T2>
Person<T1,T2>::Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
template<class T1 , class T2>
void Person<T1,T2>::showPerons()
{
cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
}
*/
void test01()
{
Person<string,int> p ("小猪" ,22);
p.showPerons();
}
int main() {
test01();
return 0;
}
1.2.8、类模板与友元
- 全局函数类实现 — 直接在类内声明友元就可以了
- 全局函数类外实现 – 需要提前让编译器知道全局函数的存在
#include <iostream>
using namespace std;
//提前让编译器知道Person类存在
template<class T1 ,class T2>
class Person;
//全局函数 类外实现
template<class T1 , class T2>
void printPerson2(Person<T1,T2> p)
{
cout << "类外实现 ------------姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
//通过全局函数 打印Person信息
template<class T1 , class T2>
class Person
{
//全局函数 类内实现
friend void printPerson(Person<T1,T2> p)
{
cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
//全局函数 类外实现
//加空模板参数列表
//如果全局函数 是类外实现,需要让编译器提前知道这个函数的存在
friend void printPerson2<>(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;
};
void test01()
{
Person<string,int> p ("猪",22);
printPerson(p);
}
void test02()
{
Person<string , int>p ("猪",22);
printPerson2(p);
}
int main() {
//test01();
test02();
return 0;
}
1.2.9、类模板案例
实现一个通用的数组类,要求如下 :
- 可以对内置数据类型以及自定义数据类型的数据进行存储
- 将数组中的数据存储到堆区
- 构造函数可以传入数组的容量
- 提供对应的拷贝构造函数以及operator=防止线拷贝问题
- 提供尾插法和尾删除法对数组中的数据进行增加和删除
- 可以通过下标的方式访问数组中的元素
- 可以获取数组中当前元素个数和数组的容量
MyArray.hpp
#ifndef _03_STL_MYARRAY_H
#define _03_STL_MYARRAY_H
//自己的通用的数组类
#include <iostream>
using namespace std;
template<class T>
class MyArray
{
public:
//有参构造 参数 容量
MyArray(int capacity)
{
// cout << "有参构造函数" << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddress = new T[this->m_Capacity];
}
//拷贝构造
MyArray(const MyArray &arr)
{
//cout << "拷贝构造函数" << endl;
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];
}
}
//operator= 防止线拷贝问题
MyArray& operator=(const MyArray &arr)
{
//cout << "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[arr.m_Capacity];
for(int i = 0 ; i < this->m_Capacity ; 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 --;
}
//通过下标方式访问数组中的元素 arr[0] = 100
T operator[] (int index)
{
return this->pAddress[index];
}
//返回数组容量
int getCapacity()
{
return this->m_Capacity;
}
//返回数组大小
int getSize()
{
return this->m_Size;
}
//析构函数
~MyArray()
{
//cout << "析构函数调用" << endl;
if(this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress;
}
}
private:
T * pAddress; //指针指向堆区开辟的真实数组
int m_Capacity; //数组容量
int m_Size; //数组大小
};
#endif //_03_STL_MYARRAY_H
- 测试代码
#include <iostream>
using namespace std;
#include "MyArray.hpp"
void printIntArray(MyArray<int> &arr)
{
for(int i = 0 ; i < arr.getSize() ; i++)
{
cout << arr[i] << endl;
}
}
void test01()
{
MyArray<int> arr1(5);
for(int i = 0 ; i < 5 ; i++)
{
//利用尾插法向数组中插入数据
arr1.Push_Back(i);
}
cout << "arr1的打印输出为:" << endl;
printIntArray(arr1);
cout << "arr1的容量为: " << arr1.getCapacity() << endl;
cout << "arr1的大小为: " << arr1.getSize() << endl;
MyArray<int> arr2 (arr1);
cout << "arr2的打印输出为:" << endl;
printIntArray(arr2);
//尾删
arr2.Pop_Back();
cout << "arr2尾删除后" << endl;
cout << "arr2的容量为: " << arr2.getCapacity() << endl;
cout << "arr2的大小为: " << arr2.getSize() << endl;
/* MyArray<int> arr3(100);
arr3 = arr1;*/
}
//测试自定义数据类型
class Person
{
public:
Person(){};
Person(string name , int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void printPerosnArray(MyArray<Person> &arr)
{
for(int i = 0 ; i < arr.getSize() ; i++)
{
cout << "姓名: " << arr[i].m_Name << " 年龄:" << arr[i].m_Age << endl;
}
}
void test02()
{
MyArray<Person> arr(10);
Person p1("小a",22);
Person p2("小b",2212);
Person p3("小c",232);
Person p4("小d",224);
Person p5("小e",212);
//将数据插入到数组中
arr.Push_Back(p1);
arr.Push_Back(p2);
arr.Push_Back(p3);
arr.Push_Back(p4);
arr.Push_Back(p5);
printPerosnArray(arr);
}
int main() {
// test01();
test02();
return 0;
}