1.模板类
#include <iostream>
using namespace std;
typedef int Item;
class Stack
{
private:
enum{MAX = 20}; // 栈的最大元素个数
Item mItems[MAX]; // 存储栈的元素的数组
int mTop; // 指针,指向栈顶待写入数据的位置,而不是栈顶的元素的位置
public:
Stack();
bool isEmpty() const; // 常量方法
bool isFull() const;
bool push(const Item& item); // 压栈
bool pop(Item& item); // 出栈
Item* getTop(); // 获得栈顶
};
Stack::Stack()
{
mTop = 0;
}
bool Stack::isEmpty() const
{
return mTop == 0; // 注意这里是两个等号,用于判断,若二者相等返回1,不等返回0
}
bool Stack::isFull() const
{
return mTop == MAX;
}
bool Stack::push(const Item& item)
{
if(!isFull())
{
mItems[mTop++] = item; // 这里mTop使用后自加,因为mTop已经指向栈顶待写入元素的位置了,在该位置写入元素后,再自加指向下一个位置,就行了
return true;
}
else
{
return false;
}
}
bool Stack::pop(Item &item)
{
if(!isEmpty())
{
item = mItems[--mTop]; // 这里用前减,先让指针指向栈顶元素位置,再出栈该元素,此时指针正好指向了栈顶元素的再往上一个位置,无需再操作
return true;
}
else
{
return false;
}
}
Item* Stack::getTop()
{
if(!isEmpty())
{
return &mItems[mTop - 1];
}
else
{
return nullptr;
}
}
int main(int argc, const char * argv[]) {
// 模板类
Stack stack;
stack.push(20);
stack.push(30);
stack.push(40);
Item item;
stack.pop(item); // 从pop的实现可以看出,出栈的时候,把出栈元素赋给了传入的参数item
cout << "item = " << item << endl; // 40
cout << "stack top = " << *stack.getTop() << endl; // 30
template <class StackType> // 模板类,其中StackType是类型占位符
class TemplateStack
{
private:
enum{MAX = 20};
StackType mItems[MAX]; // StackType是类型占位符
int mTop;
public:
TemplateStack();
bool isEmpty() const;
bool isFull() const;
bool push(const StackType& item);
bool pop(StackType& item);
StackType* getTop();
};
template <class StackType> // 每次方法的实现前面必带这个句子
TemplateStack<StackType>::TemplateStack() // 写类名字的的地方要这样写:
// TemplateStack<StackType>才行,才是模板类的方法的实现
{
mTop = 0;
}
template <class StackType>
bool TemplateStack<StackType>::isEmpty() const
{
return mTop == 0;
}
template <class StackType>
bool TemplateStack<StackType>::isFull() const
{
return mTop == MAX;
}
template <class StackType>
bool TemplateStack<StackType>::push(const StackType& item)
{
if(!isFull())
{
mItems[mTop++] = item;
return true;
}
else
{
return false;
}
}
template <class StackType>
bool TemplateStack<StackType>::pop(StackType &item)
{
if(!isEmpty())
{
item = mItems[--mTop];
return true;
}
else
{
return false;
}
}
template <class StackType>
StackType* TemplateStack<StackType>::getTop()
{
if(!isEmpty())
{
return &mItems[mTop - 1];
}
else
{
return nullptr;
}
}
int main(int argc, const char * argv[]) {
// 使用TemplateStack
TemplateStack<int> stack1;
TemplateStack<string> stack2;
stack1.push(20);
stack2.push("hello world");
cout << "stack2 top = " << *stack2.getTop() << endl; // hello world
return 0;
}
2.模板中的非类型参数
#include <iostream>
using namespace std;
template <class Type, int n> // class Type是类型参数,int n是非类型参数
class ArrayTP
{
private:
Type mValues[n];
public:
ArrayTP(Type values[]);
ArrayTP(){} // 最好是定义一个无参数的构造方法,当子类没有显式的调用父类的方法时,如果在此不定义无参数方法,那么就会报错,这里定义的就是空构造方法,以内联方式实现
virtual Type& operator[](int i); // 使用虚函数重载操作符
};
template <class Type,int n>
ArrayTP<Type,n>::ArrayTP(Type values[]) // 模板类就得用 ArrayTP<Type, n>表示类名,不能只用ArrayTP
{
for(int i = 0; i < n; i++)
{
mValues[i] = values[i];
}
}
template <class Type,int n>
Type& ArrayTP<Type,n>::operator[](int i)
{
if(i < 0 || i >= n)
{
cerr << "索引超出范围." << endl;
exit(EXIT_FAILURE);
}
return mValues[i]; // 返回的是引用
}
int main(int argc, const char * argv[]) {
// 模板中的非类型参数
int values[] = {1,2,3,4,5};
ArrayTP<int, 5> arrayTP;
arrayTP = values; // 这里会调用ArrayTP(Type values[]) 构造方法,把values[]都给到mValues[]
int value = arrayTP[2];
cout << "value = " << value << endl; // 打印 :3
cout << "arrayTP[100] = " << arrayTP[100] << endl; // 打印 :索引超出范围
return 0;
}
3.模板的继承和组合
#include <iostream>
using namespace std;
template <class T> // T是类型占位符
class TemplateClass
{
private:
T mValue;
public:
TemplateClass(T &value)
{
mValue = value;
}
T& getValue()
{
return mValue;
}
};
template <class T> // 定义子类时,也要加这句
class MyClass1 : public TemplateClass<T> // 父类名字要这样写:TemplateClass<T>,不能只写名字
{
public:
MyClass1(T &value) : TemplateClass<T>(value) // 定义构造方法时,也要加 TemplateClass<T>(value)
{
cout << "MyClass1" << endl;
}
};
template <typename T> // 和template <class T> 作用一样,也可以把typename换成class
class MyClass2
{
public:
TemplateClass<T> *templateClass;
MyClass2(T &value)
{
templateClass = new TemplateClass<T>(value);
}
~MyClass2()
{
delete templateClass;
}
};
int main(int argc, const char * argv[]) {
// 继承
string s = "hello";
MyClass1<string> myClass1(s); // 指定MyClass1中的T为string,同时构造myClass1传入字符串s即"hello"
cout << myClass1.getValue() << endl; // hello
// 组合
long n = 1234;
MyClass2<long> myClass2(n);
cout << myClass2.templateClass->getValue() << endl; // 1234
return 0;
}
4.递归使用模板
#include <iostream>
using namespace std;
template <class T>
class TemplateClass
{
private:
T mValue;
public:
TemplateClass(T &value) : mValue(value) // 当T本身是模板类时,mValue就应该在这里显式的初
// 始化,而不是在构造方法里初始化,否则会报错
{
// mValue = value;
}
T& getValue()
{
return mValue;
}
};
int main(int argc, const char * argv[]) {
// 递归模板
string s = "你好";
TemplateClass<string> templateClass(s);
// 也是模板类,只是把类型占位符T定义成了模板类型:TemplateClass<string>,
// 那么所生成的模板类的传入参数,也应该是TemplateClass<string>类型
TemplateClass<TemplateClass<string>> recursionTemplateClass(templateClass);
// 这里也要使用两个getvalue()
cout << recursionTemplateClass.getValue().getValue() << endl; // 你好
return 0;
}
5.拥有多个类型参数的模板
#include <iostream>
#include <vector>
using namespace std;
template <class T1, class T2, class T3>
class TemplateClass
{
private:
T1 mValue1;
T2 mValue2;
T3 mValue3;
public:
TemplateClass(T1 &value1, T2 &value2, T3 &value3) : mValue1(value1), mValue2(value2), mValue3(value3) // 构造方法的定义,与参数的初始化
{
}
};
int main(int argc, const char * argv[]) {
// 多类型参数的模板
int n = 100;
string s = "hello";
vector<string> v;
TemplateClass<int, string, vector<string>> templateClass(n, s, v); // 需要同时指定三个类型
return 0;
}
6.默认类型参数
#include <iostream>
using namespace std;
template <class T1, class T2 = string> // 为T2指定默认类型为字符串
class TemplateClass
{
public:
T1 value1;
T2 value2;
};
int main(int argc, const char * argv[]) {
// 默认类型参数
TemplateClass<int> templateClass1; // 此时就可以只传一个类型参数了,第二个参数默认是string
TemplateClass<int, char> templateClass2;
return 0;
}
7.模板类的显式具体化
template <typename T>
class Compare
{
private:
T mValue1;
T mValue2;
public:
// 通过构造方法将两个值传入类,同时初始化两个参数
Compare(T &value1, T &value2) : mValue1(value1), mValue2(value2)
{
}
int getResult()
{
if(mValue1 > mValue2)
{
return 1;
}
else if(mValue1 < mValue2)
{
return -1;
}
else
{
return 0;
}
}
};
int main(int argc, const char * argv[]) {
int n1 = 20, n2 = 30;
Compare<int> compareInt(n1, n2);
cout << compareInt.getResult() << endl; // -1
return 0;
}
template <typename T>
class Compare
{
private:
T mValue1;
T mValue2;
public:
// 通过构造方法将两个值传入类,同时初始化两个参数
Compare(T &value1, T &value2) : mValue1(value1), mValue2(value2)
{
}
int getResult()
{
if(mValue1 > mValue2) // 这里是两个类的比较,会调用重载后的比较操作符:>
{
return 1;
}
else if(mValue1 < mValue2)
{
return -1;
}
else
{
return 0;
}
}
};
class MyClass
{
private:
int mValue;
public:
MyClass(int value)
{
mValue = value;
}
virtual bool operator<(MyClass& myClass)
{
return mValue < myClass.mValue;
}
virtual bool operator>(MyClass& myClass)
{
return mValue > myClass.mValue;
}
};
int main(int argc, const char * argv[]) {
MyClass myClass1(30), myClass2(20);
Compare<MyClass> compareMyClass(myClass1, myClass2); // 这里是递归了模板类,把模板类作为
// T传给了Compare模板类,那么compareMyClass的两个传入参数就也应该是模板类
cout << compareMyClass.getResult() << endl;
const char *s1 = "h"; // 字符常量指针
const char *s2 = "w";
Compare<const char*> compareCharP(s1, s2); // T指定为字符常量指针,但这里比较的不是h和w
//的ASCII,而是s1和s2的地址,那么如果想比较s1和s2所指向的字符所对应的ASCII,就需要使用模板具体化
cout << compareCharP.getResult() << endl;
return 0;
}
template <typename T>
class Compare
{
private:
T mValue1;
T mValue2;
public:
// 通过构造方法将两个值传入类,同时初始化两个参数
Compare(T &value1, T &value2) : mValue1(value1), mValue2(value2)
{
}
int getResult()
{
if(mValue1 > mValue2)
{
return 1;
}
else if(mValue1 < mValue2)
{
return -1;
}
else
{
return 0;
}
}
};
// 模板具体化:阻止编译器为const char*类型生成相应的类,而是使用自己定义的类
// 即,当使用模板生成类时,若T被指定为const char*,则调用这个自定义的模板,不再调用上面的通用模板
template<> class Compare<const char*>
{
private:
const char* mValue1; // 指针
const char* mValue2;
public:
// 定义构造方法,同时传入常量字符指针,同时初始化参数
Compare(const char* value1, const char* value2):mValue1(value1), mValue2(value2)
{
}
int getResult()
{
int result = strcmp(mValue1, mValue2); // 使用了strcmp函数,传入的是指向字符串首地址的指针
if(result > 0)
{
return 1;
}
else if(result < 0)
{
return -1;
}
else
{
return 0;
}
}
};
int main(int argc, const char * argv[]) {
// 模板类的显式具体化
const char *s1 = "w";
const char *s2 = "h";
Compare<const char*> compareCharP(s1, s2);
cout << compareCharP.getResult() << endl; // 返回 1
return 0;
}
8.模板类的部分具体化
1. 类型部分具体化
#include <iostream>
using namespace std;
template <class T1, class T2>
class TemplateClass
{
private:
T1 mValue1;
T2 mValue2;
public:
TemplateClass(T1 &value1, T2 &value2)
{
mValue1 = value1;
mValue2 = value2;
cout << "通用的模板:TemplateClass(T1 &value1, T2 &value2)" << endl;
}
};
// 完全具体化
template <> class TemplateClass<int, string> // T1具体化为int类型,T2具体化为string类型
{
private:
int mValue1;
string mValue2;
public:
TemplateClass(int &value1, string &value2)
{
mValue1 = value1;
mValue2 = value2;
cout << "完全具体化:TemplateClass(int &value1, string &value2)" << endl;
}
};
// 部分具体化
template <class T1> class TemplateClass<T1, string> // 保留T1,T2具体化为string
{
private:
T1 mValue1;
string mValue2;
public:
TemplateClass(T1 &value1, string &value2)
{
mValue1 = value1;
mValue2 = value2;
cout << "部分具体化:TemplateClass(T1 &value1, string &value2)" << endl;
}
};
// 使用模板类时,编译器先考虑完全具体化的类,再考虑部分具体化,若前二者都不满足条件才会考虑通用模板
int main(int argc, const char * argv[]) {
// 模板类的部分具体化
// 1. 类型部分具体化
// 2. 指针部分具体化
int valueInt = 20;
string valueString = "Bill";
TemplateClass<int, string> templateClass1(valueInt, valueString); // 完全具体化
TemplateClass<string, string> templateClass2(valueString, valueString); // 部分具体化
TemplateClass<string, int> templateClass3(valueString, valueInt); // 通用模板
PointerTemplateClass<int> pointerTemplateClass1;
PointerTemplateClass<int*> pointerTemplateClass2;
return 0;
}
2. 指针部分具体化
把T的类型限定为指针类型
template <class T>
class PointerTemplateClass
{
public:
PointerTemplateClass()
{
cout << "通用模板" << endl;
}
};
template<class T> class PointerTemplateClass<T*>
{
public:
PointerTemplateClass()
{
cout << "指针部分具体化" << endl;
}
};
int main(int argc, const char * argv[]) {
PointerTemplateClass<int> pointerTemplateClass1; // 通用模板
PointerTemplateClass<int*> pointerTemplateClass2; // 指针部分具体化
return 0;
}
9.成员模板
即模板类中的成员还是模板类
#include <iostream>
using namespace std;
template <typename T>
class MyClass {
private:
template <typename V> // 定义模板类前面必须跟这句
class TemplateClass1
{
public:
V value;
};
template <typename V1, typename V2>
class TemplateClass2
{
public:
V1 value1;
V2 value2;
};
template<typename V1> class TemplateClass2<V1, string>//TemplateClass2的部分具体化
{
public:
V1 value1;
string value2;
TemplateClass2()
{
cout << "部分具体化" << endl;
}
};
template <typename V>
class TemplateClass3; // 只声明,未实现
public:
TemplateClass1<T> templateClass1; // 先不给T指定类型
TemplateClass2<T, int> templateClass2_1; // 不符合部分具体化,使用通用模板类
TemplateClass2<int, string> templateClass2_2; // 部分具体化
};
// 这里需要写两个这种句子,因为TemplateClass3是作为模板成员在一个模板类中的
// 上面写MyClass的句子,下面写TemplateClass3的句子
template <typename T>
template <typename V>
class MyClass<T>::TemplateClass3 // 记住这种写法
{
public:
V value;
};
int main(int argc, const char * argv[]) {
// 成员模板
MyClass<string> myClass; // Myclass的传入参数是T,即指定T为string
return 0;
}
10.将模板类作为类型参数的类型
#include <iostream>
using namespace std;
template <class TT1, class TT2>
class Person
{
};
template <class TT1>
class Person1
{
};
class TestClass
{
};
// 这个模板类的类型参数还是模板类
// 其中P是类型参数的名称,template<class T1, class T2> class是类型参数的类型
// 即,P的类型必须是模板,且这个模板必须有两个参数
template<template<class T1, class T2> class P>
class MyClass
{
};
int main(int argc, const char * argv[]) {
// 将模板类作为类型参数的类型
MyClass<Person> myClass1;
// MyClass<TestClass> myClass2; // 报错,因为TestClass的参数个数不是2
// MyClass<Person1> myClass3; // 报错,因为Person1的参数个数不是2
return 0;
}
11.模板类与友元函数
友元函数:把一个函数定义成和一个类有关的友元函数,那么在这个函数中就可以访问类的私有成员和受保护的成员
#include <iostream>
using namespace std;
template <typename T>
class TemplateClass
{
private:
T mValue;
public:
TemplateClass(T value)
{
mValue = value;
}
friend void printSize(); // 友元函数
};
template<typename T> // 模板类中的函数,在函数实现时也要加上这句
void printSize()
{
cout << "TemplateClass size:" << sizeof(TemplateClass<T>) << endl; //TemplateClass需要加上类型参数
}
int main(int argc, const char * argv[]) {
// 模板类与友元
// 1. 非模板友元
// 2. 使用类模板类型参数的友元
// 3. 使用自己的类型参数的友元
printSize<int>(); // 4 (字节)
printSize<long long>(); // 8 (字节)
return 0;
}
#include <iostream>
using namespace std;
// 第3步 在模板类的前面声明友元函数
template<typename T> void print(T& t); // 声明
template <typename T>
class TemplateClass
{
private:
T mValue;
public:
TemplateClass(T value)
{
mValue = value;
}
//其中空的尖括号表示print是使用类型参数的,后面的括号里面是在指定参数类型:TemplateClass<T>对象的引用类型
friend void print<>(TemplateClass<T>&); // 第1步 在模板类中声明友元函数
};
// 第2步 在模板类的外部实现友元函数
template<typename T>
void print(T& t)
{
cout << t.mValue << endl;
}
class MyClass
{
private:
int mCode =200;
public:
template<typename T1, typename T2> friend void show(T1&, T2&); // 这整体是一个在类中声明的函数模板,前面是声明了类型参数T1和T2,后面是友元函数
};
template <typename T1, typename T2> void show(T1& t1, T2& t2)
{
cout << t1.mCode << endl;
cout << t2 << endl;
}
int main(int argc, const char * argv[]) {
// 实现友元函数,需要完成如下3步:
// (1) 在模板类中声明友元函数
// (2) 在模板类的外部实现友元函数
// (3) 在模板类的前面声明友元函数
TemplateClass<int> templateClass(123);
print(templateClass); // 123 (这个print是简写,省略了尖括号中的参数类型)
print<TemplateClass<int>>(templateClass); // 这个是完全版
MyClass myClass;
string s = "hello world";
show(myClass, s); // 200 hello world
return 0;
}
12.模板别名(C++ 11)
#include <iostream>
#include <array>
using namespace std;
typedef array<int, 20> arri; // 用typedef为一个比较长的类型定义别名
typedef array<double, 10> arrd; // 缺点在于,当array中不想用double,想用long时,就还得用typedef定义一次,不灵活
// using
template <typename T> // 是要使用类型参数(类型占位符)T,就要写上template,否则编译器不知道是什么东西
using arr = array<T, 20>; // 这样就可以通过给arr传参,从而控制array的类型了
typedef const char* pc1;
using pc2 = const char*; // 和上式效果相同
int main(int argc, const char * argv[]) {
// 模板别名
// using 是C++ 11新提供的特性,可用于模板和非模板
pc1 p1;
pc2 p2;
arri a1;
arrd a2;
array<double, 20> a;
arr<double> a3;
arr<char> a4;
a3[0] = 12.2;
cout << a3[0] << endl;
return 0;
}