前言
这一章主要讲类模板,包括它的主要用法和作用,简单写了一下和普通类的异同。 本文主要为学习心得笔记,如有纰漏,欢迎指正
一、类模板简介
与模板函数相同,若想要将类中的某个数据类型抽象化,在使用是再指定需要的数据类型,就需要使用模板类来实现。
定义:
template<class T>
class 类名
{
};
与函数模板的区别
- 类模板一般情况下无法自动类型推导的调用方式
- 类模板在模板参数列表可以有默认参数,写法:
template<class T = 数据类型>
- 类模板在有默认参数类型时,可以发生隐式类型转化
例:
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<string> p("gzy", 'a');
Person1<string, char> p1("gzy", 'a');
p.myPrint();
}
类模板内函数的创建时机
- 普通类内的成员函数在编译时就会创建,类模板中的成员函数在调用时才会被创建,也就是说在主动调用前,编译器都看不到类模板的成员函数的内容。
- 类模板中的成员函数,里面写的东西只要语法正确,就会被编译通过,不会进行数据类型的判断
- 例:比如你类内函数的功能是对值类型数据进行数学运算,但是你传入一个数组类型,这时只要你不主动调用该函数,该函数将不会被编译器识别并报错。
二、类模板的各种应用
1.类模板对象做函数参数
- 指定类型传入
将所有模板类用到的泛型逐个写入参数列表
void printPerson1(Person<string, int> &p){}
- 参数模板化
将函数模板化,参数列表用泛型表示即可
template<class T1, class T2>
void printPerson2(Person<T1, T2> &p){}
- 整个类模板化
将整个类抽象成一个模板
template<class T>
void printPerson3(T & p){}
2.类模板的继承
如果父类是一个类模板,则子类在继承时必须指定父类T的类型
- 第一种方法,继承时指定类型
template<class T>
class Base
{
T m;
};
class Son : public Base <int> {}
- 第二种方法,将子类也变为模板,这样可以灵活指定父类类型
template<class T>
class Base
{
T m;
};
template<class T>
class Son : public Base<T> {}
3.类模板中成员函数类外实现
- 构造函数的类外实现:
相比普通构造函数类外实现,需要加上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:
直接包含.cpp源文件,include时将"xxxx.h"改为"xxxx.cpp"即可 - 解决方法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;
}
}
总结
这一章主要记录模板类的学习,模板类的使用注意事项。
|