前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】STL 容器 - STL 容器的值语意 ( 容器存储任意类型元素原理 | STL 容器元素可拷贝原理 | STL 容器元素类型需要满足的要求 | 自定义可存放入 STL 容器的元素类 )

【C++】STL 容器 - STL 容器的值语意 ( 容器存储任意类型元素原理 | STL 容器元素可拷贝原理 | STL 容器元素类型需要满足的要求 | 自定义可存放入 STL 容器的元素类 )

作者头像
韩曙亮
发布2024-01-04 08:23:15
1110
发布2024-01-04 08:23:15
举报
文章被收录于专栏:韩曙亮的移动开发专栏

一、STL 容器的 值 ( Value ) 语意

1、STL 容器存储任意类型元素原理

C++ 语言中的 STL 容器 , 可以存储任何类型的元素 , 是因为 STL 容器 使用了 C++ 模板技术进行实现 ;

C++ 模板技术 是 基于 2 次编译实现的 ;

  • 第一次编译 , 扫描模板 , 收集有关模板实例化的信息 , 生成模板头 , 进行词法分析和句法分析 ;
  • 第二次编译 , 根据实际调用的类型 , 生成包含真实类型的实例化的代码 ;

2、STL 容器元素可拷贝原理

STL 容器 定义时 , 所有的 STL 容器 的相关操作 , 如 插入 / 删除 / 排序 / 修改 , 都是 基于 值 Value 语意 的 , 不是 基于 引用 Reference 语意的 ;

  • 比如 : 向 STL 容器中 插入元素时 , 插入的都是实际的 值 Value 语意 , 不是 引用 Reference 语意 ;

如果 基于 引用 或者 指针 操作 , 假如 在外部 该 指针 / 引用 指向的对象被回收 , 那么容器操作就会出现问题 ;

STL 容器 中 , 存储的元素 , 必须是可拷贝的 , 也就是 元素类 必须提供 拷贝构造函数 ;

3、STL 容器元素类型需要满足的要求

STL 容器元素类型需要满足的要求 :

  • 提供 无参 / 有参 构造函数 : 保证可以创建元素对象 , 并存放到容器中 ;
  • 提供 拷贝构造函数 : STL 容器的元素是可拷贝的 , 这是容器操作的基础 ;
  • 提供 重载 = 操作符函数 : STL 容器的元素可以被赋值 ;

4、STL 容器迭代器遍历

除了 queue 队列容器 与 stack 堆栈容器 之外 , 每个 STL 容器都可以使用 迭代器 进行遍历 ;

  • 调用 begin() 函数 , 获取 指向 首元素 的迭代器 ;
  • 调用 end() 函数 , 获取 末尾迭代器 , 该迭代器 指向 最后一个元素的后面位置 ;

除了 queue 与 stack 容器外 , 都可以使用如下代码进行遍历 ;

代码语言:javascript
复制
	//容器的遍历
	cout << "遍历容器 :" << endl;
	for (auto it = container.begin(); it != container.end(); it++)
	{
		// 遍历当前元素 , 打印 / 判断 等操作
	}
	cout << "遍历结束" << endl;

二、代码示例 - 自定义可存放入 STL 容器的元素类

1、代码示例

STL 容器元素类型需要满足的要求 :

  • 提供 无参 / 有参 构造函数 : 保证可以创建元素对象 , 并存放到容器中 ;
  • 提供 拷贝构造函数 : STL 容器的元素是可拷贝的 , 这是容器操作的基础 ;
  • 提供 重载 = 操作符函数 : STL 容器的元素可以被赋值 ;

这里自定义 Student 类 , 需要满足上述要求 , 在 Student 类中 , 定义两个成员 , char* 类型指针 和 int 类型成员 ;

其中 char* 类型指针涉及到 堆内存 的 申请 和 释放 ;

在 有参构造 函数中 , 主要作用是 创建新对象 , 这里 直接 申请内存 , 并使用参数中的值 进行赋值 ;

代码语言:javascript
复制
	/// <summary>
	/// 创建普通构造函数
	/// </summary>
	/// <param name="name">传入的常量字符串</param>
	/// <param name="age">传入的年龄</param>
	Student(char* name, int age)
	{
		// 为 m_name 指针分配内存
		// 内存大小是传入字符串大小 + 1
		// 最后 + 1 是为了设置 \0 字符串结尾用的
		// 在析构函数中还要将该内存析构
		m_name = new char[strlen(name) + 1];
		// 将实际的值拷贝到
		// 拷贝字符串数据
		// 需添加 #define _CRT_SECURE_NO_WARNINGS 声明
		strcpy(m_name, name);
		m_age = age;
	}

在 拷贝构造函数 中 , 主要作用是 使用 现有 Student 对象 初始化新对象 , 直接申请内存 , 并将 被拷贝的对象 的值 赋值给新创建的 Student 对象 ;

代码语言:javascript
复制
	/// <summary>
	/// 拷贝构造函数
	/// 在 Student s = s2 情况下调用
	/// </summary>
	/// <param name="obj">使用该 obj 对象初始化新的 Student 对象</param>
	Student(const Student& obj)
	{
		// 为 m_name 指针分配内存
		m_name = new char[strlen(obj.m_name) + 1];
		// 拷贝字符串数据
		// 需添加 #define _CRT_SECURE_NO_WARNINGS 声明
		strcpy(m_name, obj.m_name);
		// 设置年龄
		m_age = obj.m_age;
	}

在 重载等号 = 操作符函数 中 , 主要作用是 使用 现有的 Student 对象 B 为一个 已存在的 Student 对象 A 进行赋值 , 先将 A 对象的 char* 指针释放 , 然后重新申请内存 , 最后再赋值 , int 类型的成员直接赋值 ;

代码语言:javascript
复制
	/// <summary>
	/// 重载 等号 = 操作符 函数
	/// </summary>
	/// <param name="obj">等号右边的值</param>
	/// <returns>调用者本身</returns>
	Student& operator=(const Student& obj)
	{
		//先释放 调用者 本身的 m_name 指针指向的内存
		if (m_name != NULL)
		{
			// 使用 new 分配的内存需要使用 delete 释放
			delete[] m_name;
			// 释放内存后指针置空避免野指针
			m_name = NULL;
			// 年龄也设置为默认值
			m_age = 0;
		}

		// 重新分配新的 字符串 内存
		m_name = new char[strlen(obj.m_name) + 1];
		// 拷贝字符串数据
		// 需添加 #define _CRT_SECURE_NO_WARNINGS 声明
		strcpy(m_name, obj.m_name);
		// 拷贝年龄
		m_age = obj.m_age;
		// 返回调用者本身, 以便进行链式调用
		return *this;
	}

此外 , 还有析构函数 , 在析构函数中 , 释放申请的 char* 内存 , 然后置空 ;

代码语言:javascript
复制
	~Student()
	{
		if (m_name != NULL)
		{
			// 释放使用 new 关键字分配的内存
			delete[] m_name;
			// 释放内存后的指针置空 避免野指针
			m_name = NULL;
			// 将年龄字段设置为默认值
			m_age = 0;
		}
	}

代码示例 :

代码语言:javascript
复制
// 调用 strcpy 函数需要添加该声明, 否则编译报错
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;
#include "vector"

class Student
{
public:
	/// <summary>
	/// 创建普通构造函数
	/// </summary>
	/// <param name="name">传入的常量字符串</param>
	/// <param name="age">传入的年龄</param>
	Student(char* name, int age)
	{
		// 为 m_name 指针分配内存
		// 内存大小是传入字符串大小 + 1
		// 最后 + 1 是为了设置 \0 字符串结尾用的
		// 在析构函数中还要将该内存析构
		m_name = new char[strlen(name) + 1];
		// 将实际的值拷贝到
		// 拷贝字符串数据
		// 需添加 #define _CRT_SECURE_NO_WARNINGS 声明
		strcpy(m_name, name);
		m_age = age;
	}
	~Student()
	{
		if (m_name != NULL)
		{
			// 释放使用 new 关键字分配的内存
			delete[] m_name;
			// 释放内存后的指针置空 避免野指针
			m_name = NULL;
			// 将年龄字段设置为默认值
			m_age = 0;
		}
	}

	/// <summary>
	/// 拷贝构造函数
	/// 在 Student s = s2 情况下调用
	/// </summary>
	/// <param name="obj">使用该 obj 对象初始化新的 Student 对象</param>
	Student(const Student& obj)
	{
		// 为 m_name 指针分配内存
		m_name = new char[strlen(obj.m_name) + 1];
		// 拷贝字符串数据
		// 需添加 #define _CRT_SECURE_NO_WARNINGS 声明
		strcpy(m_name, obj.m_name);
		// 设置年龄
		m_age = obj.m_age;
	}
	
	/// <summary>
	/// 重载 等号 = 操作符 函数
	/// </summary>
	/// <param name="obj">等号右边的值</param>
	/// <returns>调用者本身</returns>
	Student& operator=(const Student& obj)
	{
		//先释放 调用者 本身的 m_name 指针指向的内存
		if (m_name != NULL)
		{
			// 使用 new 分配的内存需要使用 delete 释放
			delete[] m_name;
			// 释放内存后指针置空避免野指针
			m_name = NULL;
			// 年龄也设置为默认值
			m_age = 0;
		}

		// 重新分配新的 字符串 内存
		m_name = new char[strlen(obj.m_name) + 1];
		// 拷贝字符串数据
		// 需添加 #define _CRT_SECURE_NO_WARNINGS 声明
		strcpy(m_name, obj.m_name);
		// 拷贝年龄
		m_age = obj.m_age;
		// 返回调用者本身, 以便进行链式调用
		return *this;
	}

public:
	/// <summary>
	/// 打印类的成员变量
	/// </summary>
	void print()
	{
		cout << "姓名 : " << m_name << " , 年龄 : " << m_age << endl;
	}
protected:
private:
	// 姓名
	char* m_name;

	// 年龄
	int	m_age;
};

int main() {

	Student s((char*)"Tom", 18);
	s.print();

	// 将 s 对象加入到 vec 动态数组中
	vector<Student> vec;
	vec.push_back(s);


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

	return 0;
};

2、执行结果

执行结果:

姓名 : Tom , 年龄 : 18 Press any key to continue . . .

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、STL 容器的 值 ( Value ) 语意
    • 1、STL 容器存储任意类型元素原理
      • 2、STL 容器元素可拷贝原理
        • 3、STL 容器元素类型需要满足的要求
          • 4、STL 容器迭代器遍历
          • 二、代码示例 - 自定义可存放入 STL 容器的元素类
            • 1、代码示例
              • 2、执行结果
              相关产品与服务
              容器服务
              腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档