前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】泛型编程 ⑬ ( 类模板示例 - 数组类模板 | 构造函数和析构函数 的 声明与实现 | 普通成员函数 的 声明与实现 | 外部友元函数 的 声明与实现 )

【C++】泛型编程 ⑬ ( 类模板示例 - 数组类模板 | 构造函数和析构函数 的 声明与实现 | 普通成员函数 的 声明与实现 | 外部友元函数 的 声明与实现 )

作者头像
韩曙亮
发布2023-11-24 10:18:16
3850
发布2023-11-24 10:18:16
举报

一、类模板示例 - 数组类模板


1、需求分析

类模板 的 作用就是 令 算法 和 数据类型分离 ;

本篇博客中 开始 使用 类模板 开发一个 数组类 , 数组 中 可以维护 不同类型的 元素数据 , 如 : int , char , 自定义类 ;

数组 类模板 中 , 需要开发的要素如下 :

  • 构造函数 , 初始化 数组数据 ;
  • 拷贝构造函数 , 根据一个现有的 数组类模板对象 , 创建一个新的 实例对象 ;
  • 左移 << 运算符重载 , 用于向 cout 中输出字符 , 打印数据到命令行 ; 需要使用 友元函数 在外部实现 ;
  • 下标 [] 运算符重载 , 用于读取 数组中的数据 ;
  • 等号 = 运算符重载 , 用于对比 数组实例对象 ;

数组的 数据类型 , 直接 使用 泛型 T 类型 , 这样数组就可以作为容器 , 存放任意类型的数据 ;

代码语言:javascript
复制
template <typename T>
class Array
{
private:
	// 数组长度
	int m_length;

	// 指向数组数据内存 的指针
	// 指针类型 是 泛型类型 T
	T* m_space;
};

2、构造函数和析构函数 的 声明与实现

在声明类时 , 前面加上 模板类型声明 template <typename T> , 说明在类中要使用类型 T ;

在 Array 类中 , 声明 构造函数 , 拷贝构造函数 , 析构函数 , 不需要 显示注明 类型 T ;

声明 构造函数 , 拷贝构造函数 , 析构函数 :

代码语言:javascript
复制
template <typename T>
class Array
{
public:
	// 有参构造函数
	Array(int len = 0);

	// 拷贝构造函数
	Array(const Array& array);

	// 析构函数
	~Array();
}

实现 构造函数 , 拷贝构造函数 , 析构函数 :

在 类模板 外部 访问 类模板 中声明的 函数 ,

先显示声明 模板类型 template <typename T> ,

然后在下面使用 域作用符 访问 类模板中的 函数 , 域作用符 前面的 类型 , 需要 注明实际类型 , 这里使用 Array<T>:: 访问类模板 中的 函数 ;

注意 : 如果在 函数参数 和 函数返回值 中 , 使用到了 Array 类型 , 那么也必须加上 实际类型 <T> , 否则编译时会报错 ; 在 函数体 中使用到了 Array 类型 , 可以不加 实际类型 <T> ;

构造函数 和 拷贝构造函数 中 , 创建 T 类型的数组 , 使用 m_space = new T[m_length] 代码即可 ;

代码语言:javascript
复制
// 有参构造函数
template <typename T>
Array<T>::Array(int len)
{
	// 设置数组长度
	m_length = len;

	// 为数组在堆内存中分配内存
	// 注意 元素类型为 T
	m_space = new T[m_length];

	cout << " 调用有参构造函数 " << endl;
}

// 拷贝构造函数
// 这是一个深拷贝 拷贝构造函数
template <typename T>
Array<T>::Array(const Array<T>& array)
{
	// 设置数组长度
	m_length = array.m_length;

	// 创建数组
	// 注意 元素类型为 T
	m_space = new T[m_length];

	// 为数组赋值
	for (int i = 0; i < m_length; i++)
	{
		m_space[i] = array.m_space[i];
	}

	cout << " 调用拷贝构造函数 " << endl;
}

// 析构函数
template <typename T>
Array<T>::~Array()
{
	if (m_space != NULL)
	{
		// 释放 new T[m_length] 分配的内存 
		delete[] m_space;
		m_space = NULL;
		m_length = 0;
	}

	cout << " 调用析构函数 " << endl;
}

3、普通成员函数 的 声明与实现

重载 数组下标 [] 操作符 , 使用 类模板内部 的 成员函数即可完成 ;

普通成员函数 的 声明 :

数组下标 运算符 重载 , 返回值是一个 T 类型的 数据 的引用 ;

数组下标 操作符 只有一个 操作数 , 就是 数组的 下标 , int 类型的值 ;

代码语言:javascript
复制
template <typename T>
class Array
{
public:
	// 数组下标 [] 操作符重载
	// 数组元素类型是 T 类型
	T& operator[](int i);
}

普通成员函数 的 实现 :

类模板 外部 实现 数组下标 [] 操作符重载 函数 ,

首先 , 注明 模板类型 template <typename T> , 在本次 函数实现 中需要使用 该 泛型类型 ;

然后 , 通过 域作用符 访问 函数名声 , 注意 如果遇到 函数参数 或 返回值类型 中涉及到了 数组类型 , 需要 在 类模板类型后注明实际类型 <T> ;

代码语言:javascript
复制
// 数组下标 [] 操作符重载
template <typename T>
T& Array<T>::operator[](int i)
{
	return m_space[i];
}

4、外部友元函数 的 声明与实现

重载 左移 << 操作符 , 由于 左移操作符的 做操作数是 cout 是 ostream 引用类型的 , 右操作数是 数组本身类型引用 , 返回值是 ostream 引用类型 以便可以进行链式调用 ;

因此 , 该 左移 << 操作符 不能在 类模板 内部定义 , 类模板内部定义的 操作符重载函数 , 其 左操作数 必须是 类本身 ;

外部友元函数 的 声明 :

声明时 , 需要在 函数名 和 参数列表之间 注明 泛型类型 <T>;

实现时 , 不能在 函数名 和 参数列表之间 注明 泛型类型 <T> ;

代码语言:javascript
复制
template <typename T>
class Array
{
	// 左移 << 操作符重载
	// 注意 声明时 , 需要在 函数名 和 参数列表之间 注明 泛型类型 <T>
	//		实现时 , 不能在 函数名 和 参数列表之间 注明 泛型类型 <T>
	friend ostream& operator<< <T> (ostream& out, const Array& a);
}

外部友元函数 的 实现 : 在外部 实现 类模板的 友元函数 ,

首先 , 还是注明 模板类型 , template <typename T> ;

然后 , 在 函数参数 / 返回值 类型 是 数组类型时 , 需要添加 <T> 类型标识 ;

声明时 , 需要在 函数名 和 参数列表之间 注明 泛型类型 <T>;

实现时 , 不能在 函数名 和 参数列表之间 注明 泛型类型 <T> ;

代码语言:javascript
复制
// 左移 << 操作符重载
// 注意 声明时 , 需要在 函数名 和 参数列表之间 注明 泛型类型 <T>
//		实现时 , 不能在 函数名 和 参数列表之间 注明 泛型类型 <T>
template <typename T>
ostream& operator<< (ostream& out, const Array<T>& a)
{
	for (int i = 0; i < a.m_length; i++)
	{
		// 在一行内输入数据, 使用空格隔开, 不换行
		out << a.m_space[i] << " ";
	}
	// 换行
	out << endl;
	return out;
}

二、完整代码示例


1、Array.h 头文件

代码语言:javascript
复制
#pragma once

#include "iostream"
using namespace std;

template <typename T>
class Array
{
	// 左移 << 操作符重载
	// 注意 声明时 , 需要在 函数名 和 参数列表之间 注明 泛型类型 <T>
	//		实现时 , 不能在 函数名 和 参数列表之间 注明 泛型类型 <T>
	friend ostream& operator<< <T> (ostream& out, const Array& a);

public:
	// 有参构造函数
	Array(int len = 0);

	// 拷贝构造函数
	Array(const Array& array);

	// 析构函数
	~Array();

public:
	// 数组下标 [] 操作符重载
	// 数组元素类型是 T 类型
	T& operator[](int i);

	// 等号 = 操作符重载
	Array& operator=(const Array& a);

private:
	// 数组长度
	int m_length;

	// 指向数组数据内存 的指针
	// 指针类型 是 泛型类型 T
	T* m_space;
};

2、Array.cpp 代码文件

代码语言:javascript
复制
#include "Array.h"

// 左移 << 操作符重载
// 注意 声明时 , 需要在 函数名 和 参数列表之间 注明 泛型类型 <T>
//		实现时 , 不能在 函数名 和 参数列表之间 注明 泛型类型 <T>
template <typename T>
ostream& operator<< (ostream& out, const Array<T>& a)
{
	for (int i = 0; i < a.m_length; i++)
	{
		// 在一行内输入数据, 使用空格隔开, 不换行
		out << a.m_space[i] << " ";
	}
	// 换行
	out << endl;
	return out;
}


// 有参构造函数
template <typename T>
Array<T>::Array(int len)
{
	// 设置数组长度
	m_length = len;

	// 为数组在堆内存中分配内存
	// 注意 元素类型为 T
	m_space = new T[m_length];

	cout << " 调用有参构造函数 " << endl;
}

// 拷贝构造函数
// 这是一个深拷贝 拷贝构造函数
template <typename T>
Array<T>::Array(const Array<T>& array)
{
	// 设置数组长度
	m_length = array.m_length;

	// 创建数组
	// 注意 元素类型为 T
	m_space = new T[m_length];

	// 为数组赋值
	for (int i = 0; i < m_length; i++)
	{
		m_space[i] = array.m_space[i];
	}

	cout << " 调用拷贝构造函数 " << endl;
}

// 析构函数
template <typename T>
Array<T>::~Array()
{
	if (m_space != NULL)
	{
		// 释放 new T[m_length] 分配的内存 
		delete[] m_space;
		m_space = NULL;
		m_length = 0;
	}

	cout << " 调用析构函数 " << endl;
}

// 数组下标 [] 操作符重载
template <typename T>
T& Array<T>::operator[](int i)
{
	return m_space[i];
}

// 等号 = 操作符重载
template <typename T>
Array<T>& Array<T>::operator=(const Array<T>& a)
{
	if (this->m_space != NULL)
	{
		// 释放 new int[m_length] 分配的内存 
		delete[] this->m_space;
		this->m_space = NULL;
	}

	// 设置数组长度
	this->m_length = a.m_length;

	// 创建数组
	this->m_space = new T[m_length];

	// 为数组赋值
	for (int i = 0; i < m_length; i++)
	{
		this->m_space[i] = a.m_space[i];
	}

	cout << " 调用 等号 = 操作符重载 函数" << endl;

	// 返回是引用类型
	// 返回引用就是返回本身
	// 将 this 指针解引用, 即可获取数组本身
	return *this;
}

3、Test.cpp 主函数代码文件

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

// 此处注意, 类模板 声明与实现 分开编写
// 由于有 二次编译 导致 导入 .h 头文件 类模板函数声明 无法找到 函数实现
// 必须 导入 cpp 文件
#include "Array.cpp"

int main() {

	// 验证 有参构造函数
	Array<int> array(3);

	for (int i = 0; i < 3; i++)
	{
		// 验证下标操作符
		array[i] = i;
	}

	// 验证左移操作符
	cout << array << endl;

	// 验证拷贝构造函数
	Array<int> array2 = array;

	// 验证左移操作符
	cout << array2 << endl;
	
	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
}

4、执行结果

执行结果 :

代码语言:javascript
复制
 调用有参构造函数
0 1 2

 调用拷贝构造函数
0 1 2

Press any key to continue . . .
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、类模板示例 - 数组类模板
    • 1、需求分析
      • 2、构造函数和析构函数 的 声明与实现
        • 3、普通成员函数 的 声明与实现
          • 4、外部友元函数 的 声明与实现
          • 二、完整代码示例
            • 1、Array.h 头文件
              • 2、Array.cpp 代码文件
                • 3、Test.cpp 主函数代码文件
                  • 4、执行结果
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档