【C++】泛型编程 ⑮ ( 类模板示例 - 数组类模板 | 自定义类中持有指针成员变量 )

文章目录

  • 一、支持 数组类模板 存储的 自定义类
    • 1、可拷贝和可打印的自定义类
    • 2、改进方向
    • 3、改进方向 - 构造函数
    • 4、改进方向 - 析构函数
    • 5、改进方向 - 重载左移运算符
    • 6、改进方向 - 重载拷贝构造函数 和 等号运算符
  • 二、代码示例
    • 1、Array.h 头文件
    • 2、Array.cpp 代码文件
    • 3、Test.cpp 主函数代码文件
    • 4、Test.cpp 主函数代码文件






一、支持 数组类模板 存储的 自定义类




1、可拷贝和可打印的自定义类


在上一篇博客 中 , 定义了 可拷贝 与 可打印 的 自定义类 Student , 可以被存放到 数组类模板 中 ;

由于其 成员变量 char m_name[32] 是 数组类型 , 创建时就直接分配了内存空间 , 即使浅拷贝也可以完成对 该类型对象的 拷贝工作 ;

class Student
{friend ostream& operator<<(ostream& out, const Student& s);
public:Student(){m_age = 10;strcpy(m_name, "NULL");}Student(const char* name, int age) {strcpy(this->m_name, name);this->m_age = age;}void printT() {cout << "name : " << m_name << " , age : " << m_age << endl;}private:char m_name[32];int m_age;
};// 重载左移运算符实现
ostream& operator<<(ostream& out, const Student& s) {out << "name : " << s.m_name << " , age : " << s.m_age << " ; ";return out;
}

2、改进方向


本篇博客中 , 开始讨论 自定义类 中是 char* 类型指针的情况 , 这里涉及到了 堆内存分配 以及 深拷贝 问题 ;


如果将上述 Student 类中的 char m_name[32] 数组成员 , 改为 char* m_name 指针成员 ;


那么需要进行 堆内存管理 ,

  • 在 构造函数中 分配堆内存 ;
  • 在 析构函数中 释放堆内存 ;

为了避免 浅拷贝 问题出现 , 需要

  • 进行 等号 = 运算符重载 ;
  • 以及 重写 拷贝构造函数 ;

为了使用 cout 打印该 类对象 , 需要 进行 左移 << 运算符重载 ;


3、改进方向 - 构造函数


在类的 无参构造函数 和 有参构造函数中 ,

使用 new 关键字 , 自动在堆内存中分配内存 , 然后为 堆内存 中的空间赋值 ;

	Student(){m_age = 10;// 创建一个数组个数为 1 的数组, 存放 '\0' 值// 这是一个空字符串m_name = new char[1];strcpy(m_name, "");}Student(const char* name, int age) {// 计算字符串大小// 总的大小是 字符个数 + \0 字符, 因此多一个字节int len = strlen(name) + 1;// 根据字符串大小创建 字符数组m_name = new char[len];strcpy(this->m_name, name);this->m_age = age;}

4、改进方向 - 析构函数


在析构函数中 , 需要将 使用 new 关键字申请的 堆内存进行释放 , 这里必须使用 delete 进行释放 ;

使用 malloc 申请的堆内存 , 必须使用 free 进行释放 ;

使用 new 申请的堆内存 , 必须使用 delete 进行释放 ;

	~Student(){if (m_name != NULL){delete[] m_name;m_name = NULL;}}

5、改进方向 - 重载左移运算符


重载左移运算符 , 以便可以在 cout 中打印该类信息 ;

首先 , 在类内部声明 重载左移运算符 的友元函数 ;

class Student
{friend ostream& operator<<(ostream& out, const Student& s);
}

然后 , 在 类外部 的 全局函数 中 , 实现 重载左移运算符函数 ;

// 重载左移运算符实现
ostream& operator<<(ostream& out, const Student& s) {out << "name : " << s.m_name << " , age : " << s.m_age << " ; ";return out;
}

6、改进方向 - 重载拷贝构造函数 和 等号运算符


重载拷贝构造函数 和 等号运算符 , 方便类初始化 和 使用等号赋值 ;

	Student(const Student& s) {// 计算字符串大小// 总的大小是 字符个数 + \0 字符, 因此多一个字节int len = strlen(s.m_name) + 1;// 根据字符串大小创建 字符数组m_name = new char[len];strcpy(this->m_name, s.m_name);this->m_age = s.m_age;}// 重载等号操作符Student& operator=(const Student& obj) {if (m_name != NULL) {delete[] m_name;m_name = NULL;}// 计算字符个数int len = strlen(obj.m_name) + 1;// 根据字符串大小创建 字符数组m_name = new char[len];strcpy(this->m_name, obj.m_name);this->m_age = obj.m_age;return *this;}




二、代码示例




1、Array.h 头文件


#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;// 指向数组数据内存 的指针// 指针类型 是 泛型类型 TT* m_space;
};

2、Array.cpp 代码文件


#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;// 为数组在堆内存中分配内存// 注意 元素类型为 Tm_space = new T[m_length];cout << " 调用有参构造函数 " << endl;
}// 拷贝构造函数
// 这是一个深拷贝 拷贝构造函数
template <typename T>
Array<T>::Array(const Array<T>& array)
{// 设置数组长度m_length = array.m_length;// 创建数组// 注意 元素类型为 Tm_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 主函数代码文件


#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std; // 此处注意, 类模板 声明与实现 分开编写
// 由于有 二次编译 导致 导入 .h 头文件 类模板函数声明 无法找到 函数实现
// 必须 导入 cpp 文件
#include "Array.cpp"class Student
{friend ostream& operator<<(ostream& out, const Student& s);
public:Student(){m_age = 10;// 创建一个数组个数为 1 的数组, 存放 '\0' 值// 这是一个空字符串m_name = new char[1];strcpy(m_name, "");}Student(const char* name, int age) {// 计算字符串大小// 总的大小是 字符个数 + \0 字符, 因此多一个字节int len = strlen(name) + 1;// 根据字符串大小创建 字符数组m_name = new char[len];strcpy(this->m_name, name);this->m_age = age;}Student(const Student& s) {// 计算字符串大小// 总的大小是 字符个数 + \0 字符, 因此多一个字节int len = strlen(s.m_name) + 1;// 根据字符串大小创建 字符数组m_name = new char[len];strcpy(this->m_name, s.m_name);this->m_age = s.m_age;}void printT() {cout << "name : " << m_name << " , age : " << m_age << endl;}~Student(){if (m_name != NULL){delete[] m_name;m_name = NULL;}}// 重载等号操作符Student& operator=(const Student& obj) {if (m_name != NULL) {delete[] m_name;m_name = NULL;}// 计算字符个数int len = strlen(obj.m_name) + 1;// 根据字符串大小创建 字符数组m_name = new char[len];strcpy(this->m_name, obj.m_name);this->m_age = obj.m_age;return *this;}private:char* m_name;int m_age;
};// 重载左移运算符实现
ostream& operator<<(ostream& out, const Student& s) {out << "name : " << s.m_name << " , age : " << s.m_age << " ; ";return out;
}int main() {// 验证 有参构造函数Array<Student> array(3);Student s0("Tom", 18), s1("Jerry", 12), s2("Jack", 16);array[0] = s0;array[1] = s1;array[2] = s2;// 遍历数组 打印数组元素for (int i = 0; i < 3; i++) {array[i].printT();}cout << array << endl;// 控制台暂停 , 按任意键继续向后执行system("pause");return 0;
}

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


执行结果 :

调用有参构造函数
name : Tom , age : 18
name : Jerry , age : 12
name : Jack , age : 16
name : Tom , age : 18 ; name : Jerry , age : 12 ; name : Jack , age : 16 ;

Press any key to continue . . .

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/218198.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PC端从零搭建微信自动回复机器人(一)基础框架搭建及源码

由于工作需要&#xff0c;最近一年一直在研究和使用C#&#xff0c;加上最近工作上有做微信机器人的需要&#xff0c;在已经对接、调试稳定之后&#xff0c;将项目的源码分享给大家&#xff0c;传递开源精神。 一、环境依赖 1、开发工具&#xff1a;Vistual Studio 2022 2、Ne…

什么是自动化测试po模式,po分层如何实现?

一、什么是PO模式 全称&#xff1a;page object model 简称&#xff1a;POM/PO PO模式最核心的思想是分层&#xff0c;实现松耦合&#xff01;实现脚本重复使用&#xff0c;实现脚本易维护性&#xff01; 主要分三层&#xff1a; 1.基础层BasePage&#xff1a;封装一些最基…

SpectralGPT: Spectral Foundation Model 论文翻译1

遥感领域的通用大模型 2023.11.13在CVPR发表 原文地址&#xff1a;[2311.07113] SpectralGPT: Spectral Foundation Model (arxiv.org) 摘要 ​ 基础模型最近引起了人们的极大关注&#xff0c;因为它有可能以一种自我监督的方式彻底改变视觉表征学习领域。虽然大多数基础模型…

云端导览,数字互动 | 拓世法宝AI数字人一体机助力全新旅游时代

《中国旅行消费趋势洞察白皮书&#xff08;2023版&#xff09;》显示&#xff0c;消费者旅行习惯已从“到此一游”变为“深度在地”&#xff0c;更强调在旅游中充实自我、学习新知识。 &#xff08;《中国旅行消费趋势洞察白皮书&#xff08;2023版》截图&#xff09; 从这些资…

【腾讯云 TDSQL-C Serverless 产品测评】深度实测TDSQL-C Serverless 弹性伸缩策略及稳定性

文章目录 前言一、什么是 TDSQL-C Serverless二、TDSQL-C Serverless 的弹性伸缩方案三、弹性伸缩策略及稳定性实测1.测试设计2.测试流程&#xff1a;3.测试准备工作4.开始测试5.测试结果分析5.1.整体过程分析5.2.扩容过程分析5.3.缩容过程分析 四、总结 前言 Serverless 数据…

小白也能看得懂的Jmeter性能测试中服务端资源监控技术

操作步骤&#xff1a; 1、安装插件管理器 插件管理器的作用&#xff1a;可以提供扩展插件的在线安装升级和卸载。因为我们需要在线安装监控插件&#xff0c;首先我们就要先安装插件管理器。 插件管理器的下载地址&#xff1a;https://jmeter-plugins.org/install/Install/ 如…

关于鸿蒙网络请求的问题

https://developer.huawei.com/consumer/cn/forum/topic/0204136145853212268?fid0102683795438680754 鸿蒙OS 代码 import http from ohos.net.http;export const httpUtils (url: string, data: any) > {return new Promise((resolve, reject) > {let httpRequest …

【黑马甄选离线数仓day05_核销主题域开发】

1. 指标分类 ​ 通过沟通调研&#xff0c;把需求进行分析、抽象和总结&#xff0c;整理成指标列表。指标有原子指标、派生指标、 衍生指标三种类型。 ​ 原子指标基于某一业务过程的度量值&#xff0c;是业务定义中不可再拆解的指标&#xff0c;原子指标的核心功能就是对指标…

03:2440--UART

目录 一:UART 1:概念 2:工作模式 3:逻辑电平 4:串口结构图 5:时间的计算 二:寄存器 1:简单的UART传输数据 A:GPHCON--配置引脚 B:GPHUP----使能内部上拉​编辑 C: UCON0---设置频率115200 D: ULCON0----数据格式8n1 E:发送数据 A:UTRSTAT0 B:UTXHO--发送数据输…

VCenter连接主机提示:未验证主机SSL证书的真实性

问题&#xff1a;VCenter主机断开连接了&#xff0c;重新连接主机报错SSL证书问题 移除重新加入ESXI6.0节点报错常规系统错误&#xff08;如下图&#xff09; 解决方案&#xff1a;需更改一下验证方式 VCenter Serevr设置→高级设置 将项cpxd.certmgmt.mode 值 vmca 改为&…

linux嵌入式时区问题

目录 操作说明实验参考 最近有个针对时区的需求&#xff0c;研究了下。 查询网上的一些设置&#xff0c;发现基本都是系统中自带的一些文件&#xff0c;然后开机时解析&#xff0c;或者是有个修改的命令。 操作 但针对嵌入式常用到的 busybox 制作的最小系统&#xff0c;并没…

N-134基于java实现捕鱼达人游戏

开发工具eclipse,jdk1.8 文档截图&#xff1a; package com.qd.fish;import java.awt.Graphics; import java.io.File; import java.util.ArrayList; import java.util.List;import javax.imageio.ImageIO;public class Fishes {//定义一个集合来管理鱼List<Fish> fish…