151、模板类的示例-栈
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
// typedef string DataType; // 定义栈元素的数据类型。
template <class DataType>
class Stack // 栈类
{
private:
DataType* items; // 栈数组。
int stacksize; // 栈实际的大小。
int top; // 栈顶指针。
public:
// 构造函数:1)分配栈数组内存;2)把栈顶指针初始化为 0。
Stack(int size) :stacksize(size), top(0) {
items = new DataType[stacksize];
}~Stack() {
delete [] items; items = nullptr;
}
bool isempty() const { // 判断栈是否为空。
return top == 0;
}
bool isfull() const { // 判断栈是否已满。
return top == stacksize;
}
bool push(const DataType& item) { // 元素入栈。
if (top < stacksize) { items[top++] = item; return true; }
return false;
}
bool pop(DataType& item) { // 元素出栈。
if (top > 0) { item = items[--top]; return true; }
return false;
}
};
int main()
{
Stack<string> ss(5); // 创建栈对象,大小是 5。
// 元素入栈。
// ss.push(1); ss.push(2); ss.push(3); ss.push(4); ss.push(5);
ss.push("西施"); ss.push("冰冰"); ss.push("幂幂"); ss.push("金莲");
// 元素出栈。
string item;
while (ss.isempty() == false)
{
ss.pop(item); cout << "item = " << item << endl;
}
}
152、模板类的示例-数组
类模板可以有非通用类型参数:1)通常是整型(C++20 标准可以用其它的类型);2)实例化模板
时必须用常量表达式;3)模板中不能修改参数的值;4)可以为非通用类型参数提供默认值。
优点:在栈上分配内存,易维护,执行速度快,合适小型数组。
缺点:在程序中,不同的非通用类型参数将导致编译器生成不同的类。
构造函数的方法更通用,因为数据的大小是类的成员(而不是硬编码),可以创建数组大小可变的类。
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <class T,int len=10>
class Array
{
private:
T items[len]; // 数组元素。
public:
Array() {} // 默认构造函数。
~Array() {} // 析构函数
T& operator[](int ii) { return items[ii]; } // 重载操作符[],可以修改数组中的元素。
const T& operator[](int ii) const { return items[ii]; } // 重载操作符[],不能修改数组中的元
素。
};
template <class T>
class Vector
{
private:
int len; // 数组元素的个数。
T* items; // 数组元素。
public:
// 默认构造函数,分配内存。
Vector(int size=10):len(size) {
items = new T[len];
}~Vector() { // 析构函数
delete[] items; items = nullptr;
}
void resize(int size) { // 护展数组的内存空间。
if (size <= len) return; // 只能往更大扩展。
T* tmp = new T[size]; // 分配更大的内存空间。
for (int ii = 0; ii < len; ii++) tmp[ii] = items[ii]; // 把原来数组中的元素复制到新数组。
delete[] items; // 释放原来的数组。
items = tmp; // 让数组指针指向新数组。
len = size; // 扩展后的数组长度。
}
int size() const { return len; } // 获取数组长度。
T& operator[](int ii) { // 重载操作符[],可以修改数组中的元素。
if (ii >= len) resize(ii + 1); // 扩展数组。
return items[ii];
}
const T& operator[](int ii) const { return items[ii]; } // 重载操作符[],不能修改数组中的元
素。
};
int main()
{
// Array<string,10> aa; // 创建模板类 Array 的对象。
Vector<int> aa(1); // 创建模板类 Vector 的对象。
aa[0] = 5; aa[1] = 8; aa[2] = 3; aa[3] = 2; aa[4] = 7;
// aa[0] = "西施"; aa[1] = "冰冰"; aa[2] = "幂幂"; aa[3] = "金莲"; aa[4] = "小乔";
for (int ii=0; ii<5;ii++) cout << "aa[" << ii << "]=" << aa[ii] << endl;
}
153、嵌套和递归使用模板类
在 C++11 之前,嵌套使用模板类的时候,> >之间要加空格。
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template <class DataType>
class Stack // 栈类
{
private:
DataType* items; // 栈数组。
int stacksize; // 栈实际的大小。
int top; // 栈顶指针。
public:
// 构造函数:1)分配栈数组内存;2)把栈顶指针初始化为 0。
Stack(int size = 3) :stacksize(size), top(0) {
items = new DataType[stacksize];
}~Stack() {
delete[] items; items = nullptr;
}
Stack& operator=(const Stack& v) // 重载赋值运算符函数,实现深拷贝。
{
delete[] items; // 释放原内存。
stacksize = v.stacksize; // 栈实际的大小。
items = new DataType[stacksize]; // 重新分配数组。
for (int ii = 0; ii < stacksize; ii++) items[ii] = v.items[ii]; // 复制数组中的元素。
top = v.top; // 栈顶指针。
return *this;
}
bool isempty() const { // 判断栈是否为空。
return top == 0;
}
bool isfull() const { // 判断栈是否已满。
return top == stacksize;
}
bool push(const DataType& item) { // 元素入栈。
if (top < stacksize) { items[top++] = item; return true; }
return false;
}
bool pop(DataType& item) { // 元素出栈。
if (top > 0) { item = items[--top]; return true; }
return false;
}
};
template <class T>
class Vector // 动态数组。
{
private:
int len; // 数组元素的个数。
T* items; // 数组元素。
public:
// 默认构造函数,分配内存。
Vector(int size = 2) :len(size) {
items = new T[len];
}~Vector() { // 析构函数
delete[] items; items = nullptr;
}
Vector& operator=(const Vector& v) // 重载赋值运算符函数,实现深拷贝。
{
delete[] items; // 释放原内存。
len = v.len; // 数组实际的大小。
items = new T[len]; // 重新分配数组。
for (int ii = 0; ii < len; ii++) items[ii] = v.items[ii]; // 复制数组中的元素。
return *this;
}
void resize(int size) { // 护展数组的内存空间。
if (size <= len) return; // 只能往更大扩展。
T* tmp = new T[size]; // 分配更大的内存空间。
for (int ii = 0; ii < len; ii++) tmp[ii] = items[ii]; // 把原来数组中的元素复制到新数组。
delete[] items; // 释放原来的数组。
items = tmp; // 让数组指针指向新数组。
len = size; // 扩展后的数组长度。
}
int size() const { return len; } // 获取数组长度。
T& operator[](int ii) { // 重载操作符[],可以修改数组中的元素。
if (ii >= len) resize(ii + 1); // 扩展数组。
return items[ii];
}
const T& operator[](int ii) const { return items[ii]; } // 重载操作符[],不能修改数组中的元
素。
};
int main()
{
// Vector 容器的大小缺省值是 2,Stack 容器的大小缺省值是 3。
// 创建 Vector 容器,容器中的元素用 Stack<string>。
Vector<Stack<string>> vs; // C++11 之前,>>之间要加空格。
// 手工的往容器中插入数据。
vs[0].push("西施 1"); vs[0].push("西施 2"); vs[0].push("西施 3"); // vs 容器中的第 0
个栈。
vs[1].push("西瓜 1"); vs[1].push("西瓜 2"); vs[1].push("西瓜 3"); // vs 容器中的第 1
个栈。
vs[2].push("冰冰"); vs[2].push("幂幂"); //
vs 容器中的第 2 个栈。
// 用嵌套的循环,把 vs 容器中的数据显示出来。
for (int ii = 0; ii < vs.size(); ii++) // 遍历 Vector 容器。
{
while (vs[ii].isempty() == false) // 遍历 Stack 容器。
{
string item; vs[ii].pop(item); cout << "item = " << item << endl;
}
}
// 创建 Stack 容器,容器中的元素用 Vector<string>。
Stack<Vector<string>> sv;
Vector<string> tmp; // 栈的元素,临时 Vector<string>容器。
// 第一个入栈的元素。
tmp[0] = "西施 1"; tmp[1] = "西施 2"; sv.push(tmp);
// 第二个入栈的元素。
tmp[0] = "西瓜 1"; tmp[1] = "西瓜 2"; sv.push(tmp);
// 第三个入栈的元素。
tmp[0] = " 冰 冰 1"; tmp[1] = " 冰 冰 2"; tmp[2] = " 冰 冰 3"; tmp[3] = " 冰 冰 4";
sv.push(tmp);
// 用嵌套的循环,把 sv 容器中的数据显示出来。
while (sv.isempty() == false)
{
sv.pop(tmp); // 出栈一个元素,放在临时容器中。
for (int ii = 0; ii < tmp.size(); ii++) // 遍历临时 Vector<string>容器,显示容器中每个
元素的值。
cout << " vs[" << ii << "] = " << tmp[ii] << endl;
}
// 创建 Vector 容器,容器中的元素用 Vector<string>。
Vector<Vector<string>> vv; // 递归使用模板类。
vv[0][0] = "西施 1"; vv[0][1] = "西施 2"; vv[0][2] = "西施 3";
vv[1][0] = "西瓜 1"; vv[1][1] = "西瓜 2";
vv[2][0] = "冰冰 1"; vv[2][1] = "冰冰 2"; vv[2][2] = "冰冰 3"; vv[2][3] = "冰冰 4";
// 用嵌套的循环,把 vv 容器中的数据显示出来。
for (int ii = 0; ii < vv.size(); ii++)
{
for (int jj = 0; jj < vv[ii].size(); jj++)
// cout << " vv[" << ii << "][" << jj << "] = " << vv[ii][jj] << endl;
cout << vv[ii][jj] << " ";
cout << endl;
}
}
154、模板类具体化
模板类具体化(特化、特例化)有两种:完全具体化和部分具体化。
语法请见示例程序。
具体化程度高的类优先于具体化程度低的类,具体化的类优先于没有具体化的类。
具体化的模板类,成员函数类外实现的代码应该放在源文件中。
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
// 类模板
template<class T1, class T2>
class AA { // 类模板。
public:
T1 m_x;
T2 m_y;
AA(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "类模板:构造函数。\n"; }
void show() const;
};
template<class T1, class T2>
void AA<T1, T2>::show() const { // 成员函数类外实现。
cout << "类模板:x = " << m_x << ", y = " << m_y << endl;
}
/
// 类模板完全具体化
template<>
class AA<int, string> {
public:
int m_x;
string m_y;
AA(const int x, const string y) :m_x(x), m_y(y) { cout << "完全具体化:构造函数。\n"; }
void show() const;
};
void AA<int, string>::show() const { // 成员函数类外实现。
cout << "完全具体化:x = " << m_x << ", y = " << m_y << endl;
}
/
// 类模板部分具体化
template<class T1>
class AA<T1, string> {
public:
T1 m_x;
string m_y;
AA(const T1 x, const string y) :m_x(x), m_y(y) { cout << "部分具体化:构造函数。\n"; }
void show() const;
};
template<class T1>
void AA<T1, string>::show() const { // 成员函数类外实现。
cout << "部分具体化:x = " << m_x << ", y = " << m_y << endl;
}
/
int main()
{
// 具体化程度高的类优先于具体化程度低的类,具体化的类优先于没有具体化的类。
AA<int, string> aa1(8, "我是一只傻傻鸟。"); // 将使用完全具体化的类。
AA<char, string> aa2(8, "我是一只傻傻鸟。"); // 将使用部分具体化的类。
AA<int, double> aa3(8, 999999); // 将使用模板类。
}
156、模板类与继承
1)模板类继承普通类(常见)。
2)普通类继承模板类的实例化版本。
3)普通类继承模板类。(常见)
4)模板类继承模板类。
5)模板类继承模板参数给出的基类(不能是模板类)。
示例:
1)模板类继承普通类
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class AA // 普通类 AA。
{
public:
int m_a;
AA(int a) :m_a(a) { cout << "调用了 AA 的构造函数。\n"; }
void func1() { cout << "调用了 func1()函数:m_a=" << m_a << endl;; }
};
template<class T1, class T2>
class BB:public AA // 模板类 BB。
{
public:
T1 m_x;
T2 m_y;
BB(const T1 x, const T2 y,int a) : AA(a) , m_x(x), m_y(y) { cout << "调用了 BB 的构造函数。
\n"; }
void func2() const { cout << "调用了 func2()函数:x = " << m_x << ", y = " << m_y <<
endl; }
};
int main()
{
BB<int, string> bb(8, "我是一只傻傻鸟。",3);
bb.func2();
bb.func1();
}
2)普通类继承模板类的实例化版本
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template<class T1, class T2>
class BB // 模板类 BB。
{
public:
T1 m_x;
T2 m_y;
BB(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了 BB 的构造函数。\n"; }
void func2() const { cout << "调用了 func2()函数:x = " << m_x << ", y = " << m_y <<
endl; }
};
class AA:public BB<int,string> // 普通类 AA。
{
public:
int m_a;
AA(int a,int x,string y) : BB(x,y),m_a(a) { cout << "调用了 AA 的构造函数。\n"; }
void func1() { cout << "调用了 func1()函数:m_a=" << m_a << endl;; }
};
int main()
{
AA aa(3,8, "我是一只傻傻鸟。");
aa.func1();
aa.func2();
}
3)普通类继承模板类。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template<class T1, class T2>
class BB // 模板类 BB。
{
public:
T1 m_x;
T2 m_y;
BB(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了 BB 的构造函数。\n"; }
void func2() const { cout << "调用了 func2()函数:x = " << m_x << ", y = " << m_y <<
endl; }
};
template<class T1, class T2>
class AA:public BB<T1,T2> // 普通类 AA 变成了模板类,才能继承模板类。
{
public:
int m_a;
AA(int a, const T1 x, const T2 y) : BB<T1,T2>(x,y),m_a(a) { cout << "调用了 AA 的构造函数。
\n"; }
void func1() { cout << "调用了 func1()函数:m_a=" << m_a << endl;; }
};
int main()
{
AA<int,string> aa(3,8, "我是一只傻傻鸟。");
aa.func1();
aa.func2();
}
4)模板类继承模板类。
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template<class T1, class T2>
class BB // 模板类 BB。
{
public:
T1 m_x;
T2 m_y;
BB(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了 BB 的构造函数。\n"; }
void func2() const { cout << "调用了 func2()函数:x = " << m_x << ", y = " << m_y <<
endl; }
};
template<class T1, class T2>
class AA:public BB<T1,T2> // 普通类 AA 变成了模板类,才能继承模板类。
{
public:
int m_a;
AA(int a, const T1 x, const T2 y) : BB<T1,T2>(x,y),m_a(a) { cout << "调用了 AA 的构造函数。
\n"; }
void func1() { cout << "调用了 func1()函数:m_a=" << m_a << endl;; }
};
template<class T, class T1, class T2>
class CC :public BB<T1, T2> // 模板类继承模板类。
{
public:
T m_a;
CC(const T a, const T1 x, const T2 y) : BB<T1, T2>(x, y), m_a(a) { cout << "调用了 CC 的构
造函数。\n"; }
void func3() { cout << "调用了 func3()函数:m_a=" << m_a << endl;; }
};
int main()
{
CC<int,int,string> cc(3,8, "我是一只傻傻鸟。");
cc.func3();
cc.func2();
}
5)模板类继承模板参数给出的基类
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class AA {
public:
AA() { cout << "调用了 AA 的构造函数 AA()。\n"; }
AA(int a) { cout << "调用了 AA 的构造函数 AA(int a)。\n"; }
};
class BB {
public:
BB() { cout << "调用了 BB 的构造函数 BB()。\n"; }
BB(int a) { cout << "调用了 BB 的构造函数 BB(int a)。\n"; }
};
class CC {
public:
CC() { cout << "调用了 CC 的构造函数 CC()。\n"; }
CC(int a) { cout << "调用了 CC 的构造函数 CC(int a)。\n"; }
};
template<class T>
class DD {
public:
DD() { cout << "调用了 DD 的构造函数 DD()。\n"; }
DD(int a) { cout << "调用了 DD 的构造函数 DD(int a)。\n"; }
};
template<class T>
class EE : public T { // 模板类继承模板参数给出的基类。
public:
EE() :T() { cout << "调用了 EE 的构造函数 EE()。\n"; }
EE(int a) :T(a) { cout << "调用了 EE 的构造函数 EE(int a)。\n"; }
};
int main()
{
EE<AA> ea1; // AA 作为基类。
EE<BB> eb1; // BB 作为基类。
EE<CC> ec1; // CC 作为基类。
EE<DD<int>> ed1; // EE<int>作为基类。
// EE<DD> ed1; // DD 作为基类,错误。
}
157、模板类与函数
模板类可以用于函数的参数和返回值,有三种形式:
1)普通函数,参数和返回值是模板类的实例化版本。
2)函数模板,参数和返回值是某种的模板类。
3)函数模板,参数和返回值是任意类型(支持普通类和模板类和其它类型)。
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template<class T1, class T2>
class AA // 模板类 AA。
{
public:
T1 m_x;
T2 m_y;
AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
void show() const { cout << "show() x = " << m_x << ", y = " << m_y << endl; }
};
// 采用普通函数,参数和返回值是模板类 AA 的实例化版本。
AA<int, string> func(AA<int, string>& aa)
{
aa.show();
cout << "调用了 func(AA<int, string> &aa)函数。\n";
return aa;
}
// 函数模板,参数和返回值是的模板类 AA。
template <typename T1,typename T2>
AA<T1, T2> func(AA<T1, T2>& aa)
{
aa.show();
cout << "调用了 func(AA<T1, T2> &aa)函数。\n";
return aa;
}
// 函数模板,参数和返回值是任意类型。
template <typename T>
T func(T &aa)
{
aa.show();
cout << "调用了 func(AA<T> &aa)函数。\n";
return aa;
}
int main()
{
AA<int, string> aa(3, "我是一只傻傻鸟。");
func(aa);
}
158、模板类与友元
模板类的友元函数有三类:
1)非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数。
2)约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
3)非约束模板友元:模板类实例化时,如果实例化了 n 个类,也会实例化 n 个友元函数,每个实例
化的类都拥有 n 个友元函数。
1)非模板友元示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template<class T1, class T2>
class AA
{
T1 m_x;
T2 m_y;
public:
AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
// 非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数,只能在类内实现。
friend void show(const AA<T1, T2>& a)
{
cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
}
/* friend void show(const AA<int, string>& a);
friend void show(const AA<char, string>& a);*/
};
//void show(const AA<int, string>& a)
//{
// cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
//}
//
//void show(const AA<char, string>& a)
//{
// cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
//}
int main()
{
AA<int, string> a(88, "我是一只傻傻鸟。");
show(a);
AA<char, string> b(88, "我是一只傻傻鸟。");
show(b);
}
2)约束模板友元示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
// 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
template <typename T>
void show(T& a); // 第一步:在模板类的定
义前面,声明友元函数模板。
template<class T1, class T2>
class AA // 模板类 AA。
{
friend void show<>(AA<T1, T2>& a); // 第二步:在模板类中,再次声明友元函
数模板。
T1 m_x;
T2 m_y;
public:
AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template<class T1, class T2>
class BB // 模板类 BB。
{
friend void show<>(BB<T1, T2>& a); // 第二步:在模板类中,再次声明友元函数
模板。
T1 m_x;
T2 m_y;
public:
BB(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template <typename T> // 第三步:友元函数模板的定义。
void show(T& a)
{
cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <> // 第三步:具体化版本。
void show(AA<int, string>& a)
{
cout << "具体 AA<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <> // 第三步:具体化版本。
void show(BB<int, string>& a)
{
cout << "具体 BB<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main()
{
AA<int, string> a1(88, "我是一只傻傻鸟。");
show(a1); // 将使用具体化的版本。
AA<char, string> a2(88, "我是一只傻傻鸟。");
show(a2); // 将使用通用的版本。
BB<int, string> b1(88, "我是一只傻傻鸟。");
show(b1); // 将使用具体化的版本。
BB<char, string> b2(88, "我是一只傻傻鸟。");
show(b2); // 将使用通用的版本。
}
3)非约束模板友元
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
// 非类模板约束的友元函数,实例化后,每个函数都是每个每个类的友元。
template<class T1, class T2>
class AA
{
template <typename T> friend void show(T& a); // 把函数模板设置为友元。
T1 m_x;
T2 m_y;
public:
AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template <typename T> void show(T& a) // 通用的函数模板。
{
cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <>void show(AA<int, string>& a) // 函数模板的具体版本。
{
cout << "具体<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main()
{
AA<int, string> a(88, "我是一只傻傻鸟。");
show(a); // 将使用具体化的版本。
AA<char, string> b(88, "我是一只傻傻鸟。");
show(b); // 将使用通用的版本。
}
159、成员模板类
示例:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
template<class T1, class T2>
class AA // 类模板 AA。
{
public:
T1 m_x;
T2 m_y;
AA(const T1 x, const T2 y) : m_x(x), m_y(y) {}
void show() { cout << "m_x=" << m_x << ",m_y=" << m_y << endl; }
template<class T>
class BB
{
public:
T m_a;
T1 m_b;
BB() {}
void show();
};
BB<string> m_bb;
template<typename T>
void show(T tt);
};
template<class T1, class T2>
template<class T>
void AA<T1,T2>::BB<T>::show() {
cout << "m_a=" << m_a << ",m_b=" << m_b << endl;
}
template<class T1, class T2>
template<typename T>
void AA<T1,T2>::show(T tt) {
cout << "tt=" << tt << endl;
cout << "m_x=" << m_x << ",m_y=" << m_y << endl;
m_bb.show();
}
int main()
{
AA<int, string> a(88, "我是一只傻傻鸟。");
a.show();
a.m_bb.m_a = "我有一只小小鸟。";
a.m_bb.show();
a.show("你是一只什么鸟?");
}