C++ 中的 模板类 简介


前言

这一章主要讲类模板,包括它的主要用法和作用,简单写了一下和普通类的异同。
本文主要为学习心得笔记,如有纰漏,欢迎指正


一、类模板简介

与模板函数相同,若想要将类中的某个数据类型抽象化,在使用是再指定需要的数据类型,就需要使用模板类来实现。

定义:

template<class T>		//class 可以用typename替换
class 类名
{
	//...
};

与函数模板的区别

  1. 类模板一般情况下无法自动类型推导的调用方式
  2. 类模板在模板参数列表可以有默认参数,写法:
template<class T = 数据类型>
  1. 类模板在有默认参数类型时,可以发生隐式类型转化
    例:

//有默认参数类型的类模板
template<class NameT, class IdT = int>
class Person1
{
	NameT name;
	IdT id;
public:
	Person1(NameT name, IdT id)
	{
		this->name = name;
		this->id = id;
	}
	void myPrint()
	{
		cout << name << endl;
		cout << id << endl;
	}
};

void test1()
{
	//错误用法,没有传入参数类型
	//Person1<> p("gzy", 10);	
	//Person1 p("gzy", 10);	
	
	//调用有默认参数类型的类模板
	Person1<string> p("gzy", 'a');	//会发生隐式类型转化
	Person1<string, char> p1("gzy", 'a');
	p.myPrint();
}

类模板内函数的创建时机

  • 普通类内的成员函数在编译时就会创建,类模板中的成员函数在调用时才会被创建,也就是说在主动调用前,编译器都看不到类模板的成员函数的内容。
  • 类模板中的成员函数,里面写的东西只要语法正确,就会被编译通过,不会进行数据类型的判断
  • 例:比如你类内函数的功能是对值类型数据进行数学运算,但是你传入一个数组类型,这时只要你不主动调用该函数,该函数将不会被编译器识别并报错。

二、类模板的各种应用

1.类模板对象做函数参数

  • 共三种传参方式:
  1. 指定类型传入
    将所有模板类用到的泛型逐个写入参数列表
void printPerson1(Person<string, int> &p){}
  1. 参数模板化
    将函数模板化,参数列表用泛型表示即可
template<class T1, class T2>
void printPerson2(Person<T1, T2> &p){}
  1. 整个类模板化
    将整个类抽象成一个模板
template<class T>
void printPerson3(T & p){}

2.类模板的继承

如果父类是一个类模板,则子类在继承时必须指定父类T的类型

  1. 第一种方法,继承时指定类型
	template<class T> 
	class Base 
	{ 
		T m; 
	};

	//可以直接尖括号跟后面
	class Son : public Base <int> {}
  1. 第二种方法,将子类也变为模板,这样可以灵活指定父类类型
	template<class T> 
	class Base 
	{ 
		T m; 
	};
	
	template<class T>
	class Son : public Base<T> {}

3.类模板中成员函数类外实现

  1. 构造函数的类外实现:
    相比普通构造函数类外实现,需要加上template和参数列表,作用域之后需要加上参数列表中的参数,这样才能识别是该类的函数。
template<class NameT, class IdT>
class Person
{
	NameT name;
	IdT id;
public:
	Person(NameT name, IdT id);
};

//构造函数类外实现:
template<class T1, class T2>
Person<T1, T2>::Person(){}
  • 成员函数的类外实现
    道理都差不多,也是必须加上template和参数列表,作用域后跟参数列表
template<class T1, class T2>
void Person<T1, T2>::func(){}

4.类模板的分文件编写

  • 问题:类模板中的成员函数创建时机是在调用阶段,导致分文件: 编写时链接不到。
  • 分文件:分为(.h)(.cpp)两个文件,分别存放声明和实现
  • 由于函数在调用时才会被创建,所以分文件操作后,运行会导致找不到函数的实现,即找不到cpp文件中的内容。
  1. 解决方法1:
    直接包含.cpp源文件,include时将"xxxx.h"改为"xxxx.cpp"即可
  2. 解决方法2:
    将声明和实现写在同一个文件,后缀名改为.hpp (hpp是约定名称,并非强制),调用同样调用"xxxx.hpp"

注:后面案例会写

  • 类模板的友元函数:
    最好在类内实现,类外实现比较繁琐,不建议写类外实现

注:类内成员函数加上friend关键字后就会变为全局函数


三、模拟实现模板类

注:这里写的功能比较杂,只作为参考,练习使用。

  • 以下内容均包含在 “myVector.hpp” 文件中
#pragma once
#include <iostream>
#include <string>
using namespace std;

template<class T>
class myVector
{
	T* arr;
	int capacity = 4;
	int count;
public:
	/*四个构造函数*/
	myVector();
	myVector(int capacity);
	myVector(int capacity, T ele);
	myVector(const myVector& p);

	/*析构函数*/
	~myVector();

	/*重载运算符*/
	myVector& operator= (const myVector& p);

	/*尾插*/
	void push_back(const T& val);

	/*尾删*/
	void pop_back();

	/*重载[]*/
	T& operator[] (int index);

	/*打印*/
	void print();
};

template<class T>
myVector<T>::myVector()
{
	capacity = 4;
	arr = new T[4];
	count = 0;
}

template<class T>
myVector<T>::myVector(int capacity)
{
	this->capacity = 4;
	while (this->capacity < capacity)
	{
		this->capacity <<= 1;
	}
	arr = new T[this->capacity];
	count = capacity;
}

template<class T>
myVector<T>::myVector(int capacity, T ele)
{
	while (this->capacity < capacity)
	{
		this->capacity <<= 1;
	}
	arr = new T[this->capacity];
	count = capacity;
	for (int i = 0; i < count; i++)
	{
		arr[i] = ele;
	}
}

template<class T>
myVector<T>::myVector(const myVector& p)
{
	*this = p;	//调用重载的 = 运算符
}

template<class T>
myVector<T>::~myVector() 
{
	if (arr != nullptr)
	{
		delete[] arr;
		arr = nullptr;
	}
	capacity = 0;
	count = 0;
}

template<class T>
myVector<T>& myVector<T>::operator= (const myVector& p)
{
	myVector<T>::~myVector();	//可以手动调用析构函数
	count = p.count;
	capacity = p.capacity;
	arr = new T[capacity];
	copy(p.arr, p.arr + count, arr);
	return *this;
}

template<class T>
void myVector<T>::push_back(const T& val)
{
	if (capacity <= count)	//扩容
	{
		capacity <<= 1;
		T* newArr = new T[capacity];
		copy(arr, arr + count, newArr);
		delete arr;
		arr = newArr;
	}
	arr[count] = val;
	count++;
}

template<class T>
void myVector<T>::pop_back()
{
	count -= (count > 0 ? 1 : 0);
}

template<class T>
T& myVector<T>::operator[] (int index)
{
	return arr[index];		// 使用[]来改变数值
}


template<class T>
void myVector<T>::print()
{
	for (int i = 0; i < count; i++)
	{
		cout << arr[i] << endl;
	}
}

总结

这一章主要记录模板类的学习,模板类的使用注意事项。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KamikazePilot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值