【C++】泛型编程 ⑫ ( 类模板 static 关键字 | 类模板 static 静态成员 | 类模板使用流程 )

2023-11-23 10:20:34 浏览数 (3)

将 类模板 函数声明 与 函数实现 分开进行编码 , 有 三种 方式 :

  • 类模板 的 函数声明 与 函数实现 都写在同一个类中 , 也就是没有分开进行编码 ;
  • 类模板 的 函数实现 在 类外部进行 , 函数声明 和 实现 写在相同的 .cpp 源码文件中 ;
  • 类模板 的 函数实现 在 类外部进行 , 函数声明 和 实现 写在不同的 .h 和 .cpp 源码文件中 ;

在博客 【C 】泛型编程 ⑨ ( 类模板的运算符重载 - 函数声明 和 函数实现 写在同一个类中 | 类模板 的 外部友元函数问题 ) 中实现了第一种情况 , 类模板 的 函数声明 与 函数实现 都写在同一个类中 , 也就是没有分开进行编码 ;

在博客 【C 】泛型编程 ⑩ ( 类模板的运算符重载 - 函数实现 写在类外部的同一个 cpp 代码中 | 类模板 的 外部友元函数二次编译问题 ) 中 , 分析了 第二种情况 , 类模板 的 函数实现 在 类外部进行 , 写在 一个 cpp 源码文件中 ;

在博客 【C 】泛型编程 ⑪ ( 类模板的运算符重载 - 函数实现 写在类外部的不同的 .h 头文件和 .cpp 代码中 ) 中 , 分析 第三种 情况 , 函数实现 在 类外部进行 , 函数声明 和 实现 写在不同的 .h 和 .cpp 源码文件中 ;

一、类模板使用流程


1、类模板 定义流程

类模板 定义流程 :

  • 首先 , 定义一个 使用 实际数据类型 的类 , 这里的数据类型 指的是 成员变量类型 或 成员函数 参数或返回值 类型 ; 这些类型 由于是 实际类型 , 语义明确 , 含义清楚 , 不会报错 ;
  • 然后 , 将 要使用 泛型模板 的 类型 , 改为一个 自己定义的 虚拟泛型类型 , 如 : template <typename T> 中的 T 类型 ;
  • 最后 , 在 类声明 前面加上 template <typename T> , 其中的 T 类型可以改为其它字母代替 , 一般是大写字母 ;

2、类模板 使用

使用类模板 : 使用 类模板 创建实例对象时 , 首先要注明 具体类型 , 生成具体类 , 才能创建具体类的 实例对象 , 语法格式如下 ;

代码语言:javascript复制
类模板名称<实际类型名称> 对象名称;
类模板名称<实际类型名称> 对象名称(构造函数实参列表);

以 上一篇博客的 Student 类模板为例 , 创建 Student 类模板实例对象示例如下 ;

代码语言:javascript复制
Student<int> s;
Student<int> s(1, 2);

3、类模板 函数 外部实现

如果要在 类模板 之外 实现 类模板中的 成员函数 ,

首先 , 要 声明 类模板 的类型参数 ;

代码语言:javascript复制
template <typename 类型参数列表>

然后 , 使用 域操作符 :: 访问类模板中的函数 , 访问时需要 注明 类模板的 实际参数类型 ;

代码语言:javascript复制
函数返回值类型 类模板名称<实际参数类型列表>::成员函数名(函数形参列表)
{
} 

特别注意 , 如果上面的类型中 , 涉及到的 函数返回值类型 或 函数形参列表 中 , 有 类模板类型 , 也要注明 实际的参数类型 ;

如下面的 operator 函数 ,

代码语言:javascript复制
template <typename T>
class Student
{
public:
	// 重载   运算符
	Student operator (Student& s);
};

在 类模板 外部 实现上述 函数声明 , 就需要使用如下方式 ;

代码语言:javascript复制
// 重载   运算符
// 使用  Student<T>:: 域操作符访问函数
template <typename T>
Student<T> Student<T>::operator (Student<T>& s)
{
	// 函数内部的类的 <T> 模板类型 , 可加 Student<T> 可不加 Student
	// 不加 <T> 也可以使用 , 加了也不会报错
	Student student(this->a   s.a, this->b   s.b);
	return student;
}

二、类模板 static 关键字


1、类模板 static 静态成员

类模板中 , 定义了 static 静态成员变量 ;

静态 成员变量 是属于整个类的 , 但是对于 类模板 来说 , 存在着二次编译 , 会生成多个不同的实际类 ;

类模板 的 实际类型 可能有多种 , 如 Student<int>Student<double> 是两个具体的实际类型 , C 编译器会将 类模板 编译成 两个不同的 类 ;

上述 编译成的 不同的类 , 每个类 都有一个 static 静态成员 , 相互之间是独立的 ;

2、类模板 static 关键字 用法

类模板 static 关键字 用法 :

首先 , 在 类模板 中 , 声明 static 静态成员 ;

代码语言:javascript复制
template <typename T>
class Student
{
public:
	// 类模板中的静态成员
	static T m_a;
};

然后 , 在类外部 , 初始化静态成员 , 之后才能 使用该静态成员 , 使用 静态成员 时 , 要声明 类模板 类型 template <typename T> , 下面使用 域作用符 访问时 , 需要注明 类模板类型 ;

代码语言:javascript复制
// 初始化静态成员
template <typename T>
T Student<T>::m_a = 0;

最后 , 使用 类模板 中的静态成员时 , 每个 生成的 具体类型 , 都有一个独立互不干扰的 静态成员 ; 下面 Student<int> 类型的对象 s1 , s2 共享 静态成员 m_a , Student<double> 类型的对象 s3 , s4 共享 静态成员 m_a ; s1 和 s3 是不同的静态成员 ;

代码语言:javascript复制
Student<int> s1, s2;
Student<double> s3, s4;

3、完整代码示例

代码示例 :

代码语言:javascript复制
#include "iostream"
using namespace std;

template <typename T>
class Student
{
public:
	// 类模板中的静态成员
	static T m_a;
};

// 初始化静态成员
template <typename T>
T Student<T>::m_a = 0;

int main() {
	Student<int> s1, s2;
	cout << "s1, s2 赋值前 : s1.m_a : " << s1.m_a << " , s2.m_a : " << s2.m_a << endl;

	s1.m_a = 6;
	cout << "s1, s2 赋值后 : s1.m_a : " << s1.m_a << " , s2.m_a : " << s2.m_a << endl;

	Student<double> s3, s4;
	cout << "s3, s4 赋值前 : s3.m_a : " << s3.m_a << " , s4.m_a : " << s4.m_a << endl;
	s3.m_a = 8;

	cout << "s3, s4 赋值后 : s1.m_a : " << s1.m_a << " , s2.m_a : " << s2.m_a << endl;
	cout << "s3, s4 赋值后 : s3.m_a : " << s3.m_a << " , s4.m_a : " << s4.m_a << endl;


	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
}

执行结果 :

s1, s2 赋值前 : s1.m_a : 0 , s2.m_a : 0 s1, s2 赋值后 : s1.m_a : 6 , s2.m_a : 6 s3, s4 赋值前 : s3.m_a : 0 , s4.m_a : 0 s3, s4 赋值后 : s1.m_a : 6 , s2.m_a : 6 s3, s4 赋值后 : s3.m_a : 8 , s4.m_a : 8 请按任意键继续. . .

1 人点赞