【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.mzph.cn/news/167623.shtml

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

相关文章

[网鼎杯 2020 朱雀组]phpweb

看一下源码 应该是输入的date 作为函数&#xff0c;value作为内部参数的值&#xff0c;将date()函数返回的结果显示在页面上 回去看的时候&#xff0c;意外发现页面有了新的跳转&#xff0c;观察一下发现&#xff0c;页面每隔五秒就会发生一次跳转 所以就抓包看看 抓包发现po…

GEE:kNN(k-最近邻)分类教程(样本制作、特征添加、训练、精度、最优参数、统计面积)

作者:CSDN @ _养乐多_ 本文将介绍在Google Earth Engine (GEE)平台上进行kNN(k-最近邻)分类的方法和代码,其中包括制作样本点教程(本地、在线和本地在线混合制作样本点,合并样本点等),加入特征变量(各种指数、纹理特征、时间序列特征、物候特征等),运行kNN(k-最近…

Linux中,查看Tomcat版本、如何查看Tomcat版本

方法 在tomcat的bin目录下&#xff0c;执行version.sh命令即可 结果

python每日一题——3最长连续序列

题目 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [100,4,200,1,3,2] 输出&#xf…

RpcServiceContext上下文

消费者: web 提供者: buss-service 同一服务器: 192.168.100.228 RpcServiceContext serviceContext RpcContext.getServiceContext(); //web->buss-serviceLOGGER.warn("getRequest->{}", JsonUtil.toJson(serviceContext.getRequest())); //getRequest-…

ElementUI table+dialog实现一个简单的可编辑的表格

table组件如何实现可编辑呢&#xff1f; 我的需求是把table组件那样的表格&#xff0c;实现它点击可以弹出一个框&#xff0c;然后在这个框里面输入你的东西&#xff0c;然后将他回显回去&#xff0c;当然&#xff0c;输入的有可能是时间啥的。 为什么要弹出弹层不在框上直接…

最近iphone手机的交管12123闪退,打不开的解决办法?

苹果手机系统和新版软件不配&#xff0c;终极决绝办法&#xff1a;升级IOS系统就好 可能是手机的内存不足了&#xff0c;因为在使用APP时&#xff0c;需要占用手机的内存&#xff0c;如果手机内存不足以支持软件允许&#xff0c;软件就会闪退。车主可以清理一下手机的内存&…

弹窗msvcp140_1.dll丢失的解决方法,超简单的方法分享

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中最常见的就是缺少某个文件的错误。最近&#xff0c;我在使用某些软件时&#xff0c;遇到了一个名为“msvcp140_1.dll”的错误提示。这个错误通常出现在运行某些程序时&#xff0c;由于缺少了msvcp140…

项目总结报告(案例模板)

软件项目总结报告模板套用&#xff1a; 项目概要项目工作分析经验与教训改进建议可纳入的项目过程资产 --------进主页获取更多资料-------

2023年【汽车驾驶员(中级)】最新解析及汽车驾驶员(中级)试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年汽车驾驶员&#xff08;中级&#xff09;最新解析为正在备考汽车驾驶员&#xff08;中级&#xff09;操作证的学员准备的理论考试专题&#xff0c;每个月更新的汽车驾驶员&#xff08;中级&#xff09;试题及解…

Doris中的物化视图-查询(十九)

物化视图创建完成后&#xff0c;用户的查询会根据规则自动匹配到最优的物化视图。 比如我们有一张销售记录明细表&#xff0c;并且在这个明细表上创建了三张物化视图。一个存储了不同时间不同销售员的售卖量&#xff0c;一个存储了不同时间不同门店的销售量&#xff0c;以及每…

C#,《小白学程序》第二课:数组,循环与排序

1 什么是数组&#xff1f; 数组 Array 是一组数值&#xff08;数 或 值&#xff09;。 int[] a; int[,] b; int[][] c; Anything[] d; 都是数组。 2 排序 排序就是按大小、名字、拼音或你指定的信息进行比较后排队。 排序是数组最基本的功能需求。 3 文本格式 /// <summa…

《数据结构、算法与应用C++语言描述》-代码实现散列表(线性探查与链式散列)

散列表 完整可编译运行代码&#xff1a;Github:Data-Structures-Algorithms-and-Applications/_22hash/ 定义 字典的另一种表示方法是散列&#xff08;hashing&#xff09;。它用一个散列函数&#xff08;也称哈希函数&#xff09;把字典的数对映射到一个散列表&#xff08…

spring-webflux的一些概念的理解

Spring5的webflux可以支持高吞吐量&#xff0c;使用相同的资源可以处理更加多的请求&#xff0c;它将会成为未来技术的趋势&#xff0c;但是相对于学习其他的框架相比&#xff0c;它的学习曲线很高&#xff0c;综合了很多现有的技术&#xff0c;即使按照教程学习能编写代码&…

requests库的学习(详细篇)

一、request库的安装 requests属于第三方库&#xff0c;Python不内置&#xff0c;因此需要我们手动安装。 pip3 install requests

HTML5新特性

HTML5新特性 前言语义化标签常用语义化标签优点 新增input属性新增type属性值内容其他新增input属性 video&#xff08;视频&#xff09;与audio&#xff08;音频&#xff09;标签 前言 本文主要讲解HTML5中新增了哪些内容。 语义化标签 HTML5新增了语义化标签这个特性&…

第一类曲线积分@对弧长的曲线积分

文章目录 abstract对弧长的曲线积分曲线形构件的质量第一类曲线积分曲线积分存在性利用曲线积分的定义描述曲线形构件质量问题推广曲线积分可加性闭曲线积分 曲线积分性质曲线积分的计算方法证明(部分推导) 小结曲线弧显函数形式方程下的曲线积分公式推广例例例 abstract 在积…

html table样式的设计 表格边框修饰

<!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <title>今日小说排行榜</title> <style> table {border-collapse: collapse;border: 4px double red; /*…

Python之Pygame游戏编程详解

一、介绍 1.1 定义 Pygame是一种流行的Python游戏开发库&#xff0c;它提供了许多功能&#xff0c;使开发人员可以轻松创建2D游戏。它具有良好的跨平台支持&#xff0c;可以在多个操作系统上运行&#xff0c;例如Windows&#xff0c;MacOS和Linux。在本文中&#xff0c;我们将…

单链表的反转?太细了哥们!细到离谱!

单链表的反转&#xff08;面试常出&#xff09;&#xff1a; ​ 单链表的反转&#xff0c;可以通过很多种方法实现。包括迭代法&#xff0c;递归法&#xff0c; 迭代法&#xff1a; 定义三个指针&#xff1a;prev、current和next&#xff0c;它们分别表示前一个节点、当前节点…