类模板

一、什么是类模版

类模版就是用模版参数来定义类,类模版可以用来创建通用的数据类型和函数,从而实现代码的重用。

1、语法

template <class T>
class 类模版名
{
    //类体
};

实例化时这样使用:

类模版名 <需要使用的类型名> 对象名;
MyClass<int> a;

下面是一个例子:

template<class T1, class T2>
class MyClass
{
public:
	T1 _a;		//通用类型成员变量。
	T2 _b;
	MyClass(){}
	MyClass(T1 a, T2 b):_a(a), _b(b){}
	//通用类型成员函数参数

	//通用类型用于成员函数的返回值
	T1 get1()
	{
		T1 a = 2;		//用于成员函数内变量的定义。
		return _a + a;
	}

	T2 getb()
	{
		T2 b = 1;
		return _b + b;
	}
};

int main()
{
    MyClass <int, string> a;
    return 0;
}

2、注意

  1. 在创建对象的时候,必须指定类型,否则无法创建对象。

    MyClass a; // 错误,无法创建对象
    
  2. 使用类模版时,数据类型必须适应类模版中的代码。

    //将main函数中的double改为string,调用getb(),会将a中的_b改为1,但是_b是string类型,无法赋值为int,所以会报错
    int main()
    {
        MyClass <int, string> a;
        a.getb();   //此句错误:T2 b = 1;
        return 0;
    }
    
  3. 类模版可以为通用参数指定缺省的数据类型(C11标准的函数模版也可以)。

    想使用缺省类型,只指定一个类型是行不通的:

    template<class T1, class T2>
    class MyClass{...};
    
    int main()
    {
        MyClass <int> a;        //只指定一个类型,错误
        return 0;
    }
    

    我们可以在创建类模版的时候,为通用参数指定缺省的类型。

    template<class T1, class T2 = int>
    class MyClass{...};
    
    int main()
    {
        MyClass <int> a;        //只指定一个类型,正确
        return 0;
    }
    
  4. 类的成员函数可以在类外实现。
    注意:不能在类外部的类模版成员声明上指定默认的参数模版。

    template <class T1, class T2 = double>
    class MyClass
    {
    public:
        MyClass(){}
        MyClass(T1 a, T2 b):_a(a), _b(b){}
        T1 _a;
        T2 _b;
        T1 geta();
        T2 getb();
    };
    
    template <class T1, class T2>
    //错误情况:template<class T1, class T2 = double>
    T1 MyClass<T1, T2>::geta()
    {
        return _a;
    }
    
    //错误情况:template<class T1, class T2 = double>
    template <class T1, class T2>
    
    T2 MyClass<T1, T2>::getb()
    {
        return _b;
    }
    
    
    int main()
    {
        MyClass<int> a(10,3.14);
        a.getb();
        return 0;
    }
    
  5. 可以用new创建模版对象。

    //使用默认构造函数
    MyClass<int, double> *a = new MyClass<int, double>();
    
    //指定参数
    MyClass<int, double> *b = new MyClass<int, double>(10, 3.14);
    
  6. 在程序中,模版类的成员函数使用了才会创建。
    虽然创建了类的指针,但是没有创建类的对象,所以不会调用构造函数,模版类的任何成员函数,只要程序中没有调用,就不会创建。

    template <class T1>
    class MyClass
    {
    public:
        MyClass(){_a.abcbahsj();}
        MyClass(T1 a):_a(a){_a.abcbahsj();}
        T1 _a;
    };
    
    int main()
    {
        MyClass<int> *a;        //创建指针,但是没有创建对象,所以不会调用构造函数
        return 0;
    }
    
    

二、类模版应用:栈

下面用C++实现一个顺序栈:

#include<iostream>
using namespace std;

class Stack
{
public:
	//构造函数,初始化栈,并将top改为-1
	Stack(){}
	Stack(int size) : _Size(size), _top(-1)
	{
		_data = new int[_Size];
	}
	
	~Stack()
	{
		delete[] _data;		//释放数组应该加上[]
        _data = nullptr; 	// 虽然在这里设置nullptr是多余的,但有助于表明资源已被释放
	}

	void Push(int num)
	{
		if(!Full())
		{	_top++;
			_data[_top] = num;
		}
	}

	void Pop(int &num)
	{
		if(!Empty())
		{
			num = _data[_top];
			_top--;
		}
	}

	void Print()
	{
		int temp = _top + 1;
		for(int i = 0; i < temp; i++)
		{
			cout << _data[i] << endl;
		}
	}

	bool Empty()
	{
		if(_top == -1)
		{
			return true;
		}
		return false;
	}

	bool Full()
	{
		if(_top == _Size - 1)
		{
			return true;
		}
		return false;
	}

private:
	int *_data;		//连续空间存放数据
	int _Size;		//有多少元素
	int _top;		//栈顶指针
};

int main()
{
	Stack s(10);
	s.Push(10);
	s.Push(20);
	int num = 0;
	s.Pop(num);
	s.Print();
}

引入类模版,在初始化对象的时候需要指明类模版的数据类型。

template <class T>
class Stack
{
public:
	void Push(T num)
	{
		if(!Full())
		{	_top++;
			_data[_top] = num;
		}
	}

	void Pop(T &num)
	{
		if(!Empty())
		{
			num = _data[_top];
			_top--;
		}
	}

private:
	T *_data;		//连续空间存放数据
};

int main()
{
	//使用模版类的时候要指明数据类型
	Stack<int> s(10);
}

三、类模版应用:数组

1、数组

#include<iostream>
using namespace std;

const int MAX_SIZE = 10; 

class Array
{
public:
	Array(){}
	Array(int size):  _status_size(0)
	{
		_data = new int[MAX_SIZE];
	}


	~Array()
	{
		delete[] _data;
		_data = nullptr;
	}

	//插入
	bool Insert(int pos, int num)
	{
		if (pos < 1 || pos > _status_size + 1 || IsFull()) return false;

		for(int i = _status_size; i >= pos; i--)
		{
			_data[i] = _data[i - 1];
		}
		_data[pos - 1] = num;
		_status_size++;

		return true;
	}

	//删除
	bool Delete(int pos)
	{
		if(pos < 1 || pos > _status_size)	return false;
		if(IsEmpty())	return false;
		for(int i = pos; i < _status_size; i++)
		{
			_data[i - 1] = _data[i];
		}
		_status_size--;
		return true;
	}

	//更改
	int &operator[](int i)
	{
		return _data[i];
	}

	//访问
	const int &operator[](int i) const 
	{
		return _data[i];
	}

	//查询
	int Search(int pos)
	{
		if(pos < 1 || pos > _status_size)	return false;
		return _data[pos - 1];
	}

	//判满
	bool IsFull()
	{
		if(_status_size == MAX_SIZE)
		{
			return true;
		}
		return false;
	}

	//判空
	bool IsEmpty()
	{
		if(_status_size == 0) 	return true;
		return false;
	}

	//显式
	void Show()
	{
		for(int i = 0; i < _status_size; i++)
		{
			cout << _data[i] << endl;
		}
	}

	//扩容
public:
	int *_data;
	int _status_size;		//
};

int main()
{
	Array a(10);
	a.Insert(1,10);
	a.Insert(1,20);
	a.Insert(1,30);
	a.Insert(1,40);
	a.Show();
	cout << "a[2] = " << a[2] << endl;
	return 0;
}

可以在类模版中加入数组大小,就不用再定义一个常量了。

#include<iostream>
using namespace std;

const int MAX_SIZE = 10; 

template <class T, int len = 10>
class Array
{
public:
	Array(){}
	Array(int size):  _status_size(0)
	{
		_data = new T[len];
	}


	~Array()
	{
		delete[] _data;
		_data = nullptr;
	}

	//插入
	bool Insert(int pos, T num)
	{
		if (pos < 1 || pos > _status_size + 1 || IsFull()) return false;

		for(int i = _status_size; i >= pos; i--)
		{
			_data[i] = _data[i - 1];
		}
		_data[pos - 1] = num;
		_status_size++;

		return true;
	}

	//删除
	bool Delete(int pos)
	{
		if(pos < 1 || pos > _status_size)	return false;
		if(IsEmpty())	return false;
		for(int i = pos; i < _status_size; i++)
		{
			_data[i - 1] = _data[i];
		}
		_status_size--;
		return true;
	}

	//更改
	T &operator[](int i)
	{
		return _data[i];
	}

	//访问
	const T &operator[](int i) const 
	{
		return _data[i];
	}

	//查询
	T Search(int pos)
	{
		if(pos < 1 || pos > _status_size)	return false;
		return _data[pos - 1];
	}

	//判满
	bool IsFull()
	{
		if(_status_size == len)
		{
			return true;
		}
		return false;
	}

	//判空
	bool IsEmpty()
	{
		if(_status_size == 0) 	return true;
		return false;
	}

	//显式
	void Show()
	{
		for(int i = 0; i < _status_size; i++)
		{
			cout << _data[i] << endl;
		}
	}

	//扩容


public:
	T *_data;
	int _status_size;
};

int main()
{
	Array<char> a(10);		//使用缺省的数组大小
	a.Insert(1,'a');
	a.Insert(1,'b');
	a.Insert(1,'c');
	a.Insert(1,'d');
	a.Show();
	cout << "a[2] = " << a[2] << endl;
	return 0;
}

2、类模版非通用类型参数规则

类模版可以有非通用类型参数,规则如下:

  1. 通常是整型(C++20可以使用其他)
  2. 实例化模版时必须使用常量表达式
  3. 模版中不能修改参数的值
#include <iostream>
#include <cstddef> // for size_t

// 1) 类模板定义,其中T是类型参数,N是非类型参数
//(在C++20之前,N通常是整型;C++20开始,可以使用其他类型,但整型仍最常见)
template <typename T, size_t N>
class MyArray 
{
private:
    T array[N]; // 使用非类型参数N来定义数组的大小

public:
    MyArray() 
	{
        for (size_t i = 0; i < N; ++i) 
		{
            array[i] = T(); 	// 将每个元素初始化为其类型的默认值
        }
    }

    void print() const 
	{
        for (size_t i = 0; i < N; ++i) 
		{
            std::cout << array[i] << " ";
        }
        std::cout << std::endl;
    }

    // 3) 在模板内部,不能修改非类型参数N的值
    // 下面的方法是错误的示例,用于说明不能修改非类型模板参数
    // void resize(size_t newSize) 
	{
    //     N = newSize; 	// 错误:在模板内部不能修改非类型参数N的值
    // }

};

int main() 
{
    // 2) 实例化模板时,非类型参数N必须使用常量表达式
    // 这里,5是一个常量表达式,因此可以用于实例化MyArray模板
    MyArray<int, 5> myArray;

    myArray.print();

    // 注意:在myArray对象内部,我们不能修改N的值
    // 例如,下面的代码是错误的,因为它试图修改一个编译时常量
    // myArray.resize(10);

    return 0;
}

四、模版的嵌套和递归

创建一个Vector缺省大小为2,Stack为3
创建一个Vector,容器的元素使用Stack

#include<iostream>
using namespace std;

template <class T>
class Vector
{
private:
	int len;
	T *_data;
public:
	Vector(int size = 10) : len(size)
	{
		_data = new T[len];
	}

	~Vector()
	{
		delete[]_data;
		_data = nullptr;
	}

	//扩展内存空间
	void resize(int size = 2)
	{
		if(size <= len)	return;
		T* tmp = new T[size];
		for(int i = 0; i < len; i++)
		{
			tmp[i] = _data[i];			
			//此处浅拷贝,复制的是类,类中使用堆区内存,就不对
		}
		delete [] _data;
		_data = tmp;
		len = size;
	}

	int size() const 
	{
		return len;
	}

	T &operator[](int i)
	{
		if(i >= len)
		{
			resize(i + 10);
		}
		return _data[i];
	}

	const T& operator[](int i) const
	{
		return _data[i];
	} 

};

template <class T>
class Stack
{
public:
	//构造函数,初始化栈,并将top改为-1
	Stack() : _Size(3), _top(-1)
	{
		_data = new T[_Size];
	}
	Stack(int size) : _Size(size), _top(-1)
	{
		_data = new T[_Size];
	}
	
	~Stack()
	{
		delete[] _data;		//释放数组应该加上[]
        _data = nullptr; 	// 虽然在这里设置nullptr是多余的,但有助于表明资源已被释放
	}

	void Push(T num)
	{
		if (!Full())
		{
			_data[++_top] = num;  // 先增加 _top,然后赋值
		}
		else
		{
			// 可以选择抛出异常、记录错误、或者动态调整栈大小
			throw std::overflow_error("Stack is full");
		}
	}

	void Pop(T &num)
	{
		if(!Empty())
		{
			num = _data[_top];
			_top--;
		}
	}

	void Print()
	{
		int temp = _top + 1;
		for(int i = 0; i < temp; i++)
		{
			cout << _data[i] << endl;
		}
	}

	bool Empty()
	{
		if(_top == -1)
		{
			return true;
		}
		return false;
	}

	bool Full()
	{
		if(_top == _Size - 1)
		{
			return true;
		}
		return false;
	}

	Stack& operator=(const Stack& s)		//深拷贝
	{
		delete[] _data;				//释放原内存
		_Size = s._Size;			
		_data = new T[_Size];		//分配新内存
		for(int i = 0; i < _Size; i++)
		{
			_data[i] = s._data[i];
		}
		_top = s._top;
		return *this;
	}

private:
	T *_data;		//连续空间存放数据
	int _Size;		//有多少元素
	int _top;		//栈顶指针
};

int main()
{
	//创建一个Vector缺省大小为2,Stack为3
	//创建一个Vector,容器的元素使用Stack

	Vector<Stack<string>> v;		//相当于创建了一个数组,数组中的每个元素都是一个栈
	Stack<string> v1[2];
	string v2[2][3];

	//1)
	//Vector<Stack<string>> v;
	//这行代码声明了一个名为 v 的变量,它是 Vector 的一个实例,
	//而 Vector 存储的元素类型是 Stack<string>。
	//这里 Stack<string> 表示一个栈,栈中的元素是字符串类型(string)。
	//在C++11之前,当使用两个连续的尖括号(>>)时,
	//需要在它们之间添加一个空格以避免解析错误,
	//因为编译器可能会将 >> 误解为右移操作符。从C++11开始,这个空格不是必需的。

	//2)
	//Stack<string> v1[2];
	//这行代码声明了一个名为 v1 的数组,数组中有两个元素,
	//每个元素都是 Stack<string> 类型。
	//这意味着 v1 是一个包含两个栈的数组,每个栈都可以存储字符串。

	//3)
	//string v2[2][3];
	//这行代码声明了一个名为 v2 的二维数组,
	//数组的大小是 2x3,即两行三列,数组中的元素类型是 string。
	//这意味着 v2 可以存储 6 个字符串,排列成两行三列的形式。
	//手工插入数据
	v[0];	
	v[0].Push("小1"); 
	v[0].Push("小2"); 
	v[0].Push("小3");		//v容器中的第0个栈
	v[1];	v[1].Push("小1"); v[1].Push("小2"); v[1].Push("小3");		//v容器中的第1个栈
	v[2].Push("哈哈哈");	v[2].Push("棒棒");
	//如何将这个嵌套容器全部显式呢?用嵌套循环
	for(int i = 0; i < v.size(); i++)		//遍历Vector容器
	{
		while(v[i].Empty() == false)		//栈不为空栈
		{
			string temp;
			v[i].Pop(temp);
			cout << "data = " << temp << endl;
		}
	}
}

五、类模版的具体化

类模版的具体化有两种:
完全具体化和部分具体化

具体化程度高的类 > 具体化程度低的类
具体化的类 > 没有具体化的类。

#include<iostream>
using namespace std;

//没有具体化的类
template<class T1, class T2>
class A
{
public:
	T1 _x;
	T2 _y;

	A(const T1 x, const T2 y):_x(x),_y(y)
	{
		cout << "类模板:构造函数\n" << endl;
	}

	void show() const;
};

template<class T1, class T2>
void A<T1, T2>::show() const 
{
	cout << "类模板 x = " << _x << " y = " << _y << endl;
}

模板函数完全具体化
template<>
class A<int , string>
{
public:
	int _x;
	string _y;

	A(const int x, const string y): _x(x), _y(y)
	{
		cout << "完全具体化:构造函数" << endl;
	}

	void show()const;
};

void A<int , string>::show() const 
{
	cout << "完全具体化 x = " << _x << " y = " << _y << endl;
}

//类模板部分显示具体化
template<class T1>
class A<T1, string>
{
public:
	T1 _x;
	string _y;
	
	A(const T1 x, const string y):_x(x), _y(y)
	{
		cout << "部分具体化:构造函数" << endl;
	}

	void show()const;
};

template <class T1>
void A<T1, string>::show() const
{
	cout << "部分具体化 x = " << _x << " y = " << _y << endl; 
}

int main()
{
	A<int ,string> a(8, "hhh");		//完全具体化的版本
	a.show();

	//接着注释掉完全具体化的版本,在此构造
	A<int ,string> b(8, "hhh");		//部分具体化的版本
	b.show();

	//接着注释掉部分具体化的版本
	A<int ,string> c(8, "hhh");
	c.show();						//没有具体化的版本
}

六、模板类与继承

  1. 模板类继承普通类,在派生类中指明基类的构造函数即可。
    #include<iostream>
    using namespace std;
    class A
    {
    public:
    	int _a;
    	A(int a) : _a(a)
    	{
    		cout << "调用了A的构造函数" << endl;
    	}
    
    	void func1()
    	{
    		cout << "调用了func1()函数" << endl;
    	}
    };
    
    template <class T1, class T2>
    class B: public A
    {
    public:
    	T1 _x;
    	T2 _y;
    	B(const T1 x, const T2 y): _x(x), _y(y)
    	{
    		cout << "调用了B的构造函数" << endl;
    	}
    
    	void func2()
    	{
    		cout << "调用了func2()函数 x = " << _x << " y = " << _y << endl;
    	}
    };
    
    int main()
    {
    	B<int, int> b(10, 20);
    	b.func2();
    	
    	return 0;
    }
    
    此时会报错,显示A没有默认的构造函数,需要我们在派生类的构造函数的初始化列表中指明基类的构造函数。
    B(const T1 x, const T2 y, int a): A(a), _x(x), _y(y)
    
  2. 普通类继承模板类的实例版本,需要在派生类中指定基类的实例版本,并且在派生类的构造函数中指明基类的构造函数。
    template <class T1, class T2>
    class B
    {
    public:
    	T1 _x;
    	T2 _y;
    	B(const T1 x, const T2 y): _x(x), _y(y)
    	{
    		cout << "调用了B的构造函数" << endl;
    	}
    
    	void func2()
    	{
    		cout << "调用了func2()函数 x = " << _x << " y = " << _y << endl;
    	}
    };
    
    class A: public B<int, int>
    {
    public:
    	int _a;
    	A(int a, int x, int y) :B(x, y) , _a(a)
    	{
    		cout << "调用了A的构造函数" << endl;
    	}
    
    	void func1()
    	{
    		cout << "调用了func1()函数" << endl;
    	}
    };
    
    int main()
    {
    	A a(10, 20 ,30);
    	a.func1();
    	return 0;
    }
    
  3. 普通类继承模板类。
    template <class T1, class T2>
    class B
    {
    public:
    	T1 _x;
    	T2 _y;
    	B(const T1 x, const T2 y): _x(x), _y(y)
    	{
    		cout << "调用了B的构造函数" << endl;
    	}
    
    	void func2()
    	{
    		cout << "调用了func2()函数 x = " << _x << " y = " << _y << endl;
    	}
    };
    
    template <class T1, class T2>
    class A: public B<T1, T2>
    {
    public:
    	int _a;
    	A(int a, const T1 x, const T2 y) :B<T1, T2>(x, y) , _a(a)
    	{
    		cout << "调用了A的构造函数" << endl;
    	}
    
    	void func1()
    	{
    		cout << "调用了func1()函数" << endl;
    	}
    };
    
    int main()
    {
    	A<int, int> a(10, 20 ,30);
    	a.func1();
    	return 0;
    }
    
  4. 模板类继承模板类
    template <class T1, class T2>
    class B
    {
    public:
    	T1 _x;
    	T2 _y;
    	B(const T1 x, const T2 y): _x(x), _y(y)
    	{
    		cout << "调用了B的构造函数" << endl;
    	}
    
    	void func2()
    	{
    		cout << "调用了func2()函数 x = " << _x << " y = " << _y << endl;
    	}
    };
    
    template <class T, class T1, class T2>
    class C:public B<T1, T2>
    {
    public:
    	T _a;
    	C(){}
    	C(const T a, const T1 b, const T2 c): B<T1, T2>(10, 20), _a(a)
    	{
    		cout << "调用了C的构造函数" << endl;
    	}
    	void func3()
    	{
    		cout << "调用了func3()函数" << endl;
    	}
    };
    
    int main()
    {
    	C<int, int, int> c(10, 20, 30);
    	return 0;
    }
    
  5. 模板类继承模板参数给出的基类(不能是模板类)
    #include<iostream>
    using namespace std;
    
    class A
    {
    public:
    	A()
    	{
    		cout <<  "调用了A()" << endl;
    	}
    	A(int a)
    	{
    		cout <<  "调用了A(int a)" << endl;
    	}
    };
    
    class B
    {
    public:
    	B()
    	{
    		cout <<  "调用了B()" << endl;
    	}
    	B(int b)
    	{
    		cout <<  "调用了B(int b)" << endl;
    	}
    };
    
    class C
    {
    public:
    	C()
    	{
    		cout <<  "调用了C()" << endl;
    	}
    	C(int c)
    	{
    		cout <<  "调用了C(int c)" << endl;
    	}
    };
    
    template<class T>		//D是模板类
    class D
    {
    public:
    	D(){cout << "调用了D的构造函数D()" << endl;}
    	D(int a) {cout << "调用了D的构造函数D(int a)" << endl;}
    };
    
    template<class T>
    class E :public T		//模板类继承模板参数给出的基类
    {
    public:
    	E():T()
    	{
    		cout << "调用了E的构造函数" << endl;
    	}
    	E(int a):T(a)
    	{
    		cout << "调用了E的构造函数E(int a)" << endl;
    	}
    };
    
    int main()
    {
    	E<A> ea1;		//A作为基类
    	E<B> eb1;		//B作为基类
    	E<C> ec1; 		//C作为基类
    	//E<D<int>> ed1;	//D<int>作为基类
    
    	E<D> ed1;		//报错,DD不能直接作为基类
    	return 0;
    }
    

七、模板类和函数

模板类可以用于函数的参数和返回值。

  1. 普通函数,参数和返回值是模板类的实例化版本。

    template <class T1, class T2>
    class A
    {
    public:
    	A(const T1 x, const T2 y):_x(x), _y(y){}
    	T1 _x;
    	T2 _y;
    	void show() const
    	{
    		cout << "show() x = " << _x <<" y = " << _y << endl;
    	}
    };
    
    //采用普通函数,参数和返回值是模板类的实例化版本。
    A<int, string> func(A<int, string> &a)
    {
    	a.show();
    	cout << "调用了func(A<int, string> &a" << endl;
    	return a;
    }
    
    int main()
    {
    	A<int, string> a(3, "aaa");
    	func(a);	//参数是模板类的实例化版本
    }
    

    如果将main函数中的int和string换成其他类型,会报错。

    A<double, char> a(3.14, 'a');	//报错
    

    需要将普通函数func()转换为函数模板。

  2. 函数模板,参数和返回值是某种的模板类。

    //函数模板,参数和返回值是模板类A
    template<typename T1, typename T2>
    A<T1, T2> func(A<T1, T2> &a)
    {
    	a.show();
    	cout << "调用了func(A<T1, T2> &a)" << endl;
    	return a;
    }
    
    int main()
    {
    	A<char, int> a('r', 100);
    	func(a);	
    }
    

    注意:如果普通函数和函数模板同时存在,优先调用普通函数。

  3. 函数模板,参数和返回值是任意类型(支持普通类和模板类和其他类型)。

    class B
    {
    public:
    	void show(){cout << "调用了B的show()方法" << endl;}
    };
    
    //函数模板,参数和返回值是任意类型
    template <typename T>
    T func(T &t)
    {
    	t.show();
    	cout << "调用了func(T &t)" << endl;
    	return t;
    }
    
    int main()
    {
    	B b;
    	func(b);
    	return 0;	
    }
    

    参数是仿函数:

    class B
    {
    public:
    	void operator()()	//重载了()运算符(仿函数)
    	{
    		cout << "调用了BB类的仿函数" << endl;
    	}
    };
    
    //函数模板,参数和返回值是任意类型
    template <typename T>
    T func(T t)
    {
    	t();
    }
    
    int main()
    {
    	B b;
    	func(b);
    	return 0;	
    }
    

    参数是函数指针

    void show()
    {
    	cout << "调用了show()函数" << endl;
    }
    
    //函数模板,参数和返回值是任意类型
    template <typename T>
    T func(T t)
    {
    	t();
    }
    
    int main()
    {
    	func(show);
    	return 0;	
    }
    

八、模板类与友元

  1. 非模版友元:友元函数不是模版函数,而是利用模版类参数生成的函数。

    template<class T1, class T2>
    class A
    {
    private:
    	T1 _x;
    	T2 _y;
    public:
    	A(const T1 x, const T2 y): _x(x), _y(y){}
    
    	//此处这个show()并非函数模版,而是用模版类参数生成的函数。因为下面具体化的函数不能和他并存,并且只能在这个类中使用。
    
    	//两个show()并存会报错
    	friend void show(A<T1, T2>& a)
    	{
    	cout << "_x = " << a._x << " _y = " << a._y << endl;
    	}
    	
    	friend void show(A<int, string> &a);
    };
    
    void show(A<int, string> &a)
    {
    	cout << "_x = " << a._x << " _y = " << a._y << endl;
    }
    
    int main()
    {
    	A<int, int> a(10, 20);
    	show(a);
    
    	A<int, string> b(10,"hhhh");
    	return 0;
    }
    
  2. 约束模版友元:模版类实例化时,每个实例化的类对应一个友元函数。

    #include<iostream>
    using namespace std;
    
    //约束模版友元:模版类实例化时,每个实例化的类对应一个友元函数
    template <typename T>
    void show(T &a);							//第一步:在模版类的前面,声明友元函数模版,让类知道
    
    template<class T1, class T2>
    class A
    {
    private:
    	T1 _x;
    	T2 _y;
    	friend void show<>(A<T1, T2> &a);		//第二部:在模版类中,再次声明友元函数模版,让编译器知道
    	//编译器在实例化某种数据类型的模版类时,也会实例化这种数据类型的模版函数
    public:
    	A(const T1 x, const T2 y):_x(x), _y(y){}
    };
    
    template <typename T>						//第三部:定义友元函数模版,可以与具体化函数共存
    void show(T &a)
    {
    	cout << "通用:x = " << a._x << " y = " << a._y << endl;
    }
    
    template<>
    void show(A<int, string> &a)
    {
    	cout << "具体化A<int, string>: x = " << a._x << " y = " << a._y << endl;
    }
    
    int main()
    {
    	A<int, string> a(10, "hhh");
    
    	show(a);		//调用具体化版本
    	//show(a);		//注释掉具体化的show函数后,调用通用版本
    	return 0;
    }
    

这种友元的函数模版可以用于多个模版类。

//第一步:在模版类的前面,声明友元函数模版,让类知道
	template <typename T>
	void show(T &a);							

	template<class T1, class T2>
	class A
	{
	private:
		T1 _x;
		T2 _y;

		//第二步:在模版类中,再次声明友元函数模版,让编译器知道
		//编译器在实例化某种数据类型的模版类时,也会实例化这种数据类型的模版函数
		friend void show<>(A<T1, T2> &a);		
		
	public:
		A(const T1 x, const T2 y):_x(x), _y(y){}
	};

	template<class T1, class T2>
	class B
	{
	private:
		T1 _x;
		T2 _y;
		friend void show<>(B<T1, T2> &b);		
	public:
		B(const T1 x, const T2 y):_x(x), _y(y){}
	};


	//第三步:定义友元函数模版,可以与具体化函数共存
	template <typename T>						
	void show(T &t)
	{
		cout << "通用:x = " << t._x << " y = " << t._y << endl;
	}

	int main()
	{
		A<char, string> a('a',"hhhhh");
		show(a);
		B<string, string> b("nihao", "hi");
		show(b);
		return 0;
	}
  1. 非约束模版友元(实际开发中不用):模版类实例化时,如果实例化了n个类,也会实例化n个友元函数,每个实例化的类都拥有n个友元函数。

    #include<iostream>
    using namespace std;
    
    
    //非类模版约束的友元函数,实例化后,每个函数都是每个类的友元
    template<class T1, class T2>
    class A
    {
    private:
    	template<typename T>
    	friend void show(T &a);			//函数模版show成为A的友元函数
    
    	T1 _x;
    	T2 _y;
    public:
    	A(const T1 x, const T2 y):_x(x), _y(y){}
    };
    
    template<typename T>
    void show(T &a)						//通用的函数模版
    {
    	cout << "通用 x = " << a._x << " y = " << a._y << endl;
    }
    
    template<>
    void show(A<int, string> &a)		//函数模版的具体化
    {
    	cout << "具体化 x = " << a._x << " y = " << a._y << endl;
    }
    
    int main()
    {
    	A<int, string> a(10, "hhh");		//调用具体化版本
    	show(a);
    
    	A<char, int> b('a', 10000);			//调用通用版本
    	show(b);
    	return 0;
    }
    

九、类模版的成员模版

类模版中可以定义成员模版,成员模版也可以是函数模版,也可以是类模版。

#include <iostream>
using namespace std;


template<class T1, class T2>
class A
{
public:
	T1 _x;
	T2 _y;

	A(const T1 x, const T2 y):_x(x), _y(y){}

	void show()
	{
		cout << "x = " << _x << " y = " << _y << endl;
	}

	template <class T>
	class B
	{
	public:
		T _a;
		T1 _b;
		B(){}
		void show()
		{
			cout << "_a = " << _a << " _b = " << _b << endl;
		}
	};

	B<string> b;

	template<typename T>
	void show(T t)		//与show参数不同,可以重载
	{
		cout << "t = " << t << endl;
		cout << "_x = " << _x << " _y = " << _y << endl;
		b.show();
	}
};

int main()
{
	A<int, string> a(10, "hhh");
	a.show(10);		
}

类成员的成员模版,定义也可以放到类的外面

...	//前面不做改动
	template <class T>
	class B
	{
	public:
		T _a;
		T1 _b;
		B(){}
		void show();	//此处仅声明
	};

	template<class T1, class T2>		//放A的模版参数
	template <class T>					//放B的模版参数	
	void A<T1, T2>::B<T>::show()		//此处定义
	{
		cout << _a << " " << _b << endl;
	}
posted @ 2025-01-17 11:02  baobaobashi  阅读(47)  评论(0)    收藏  举报