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

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

时间:2024-01-08 15:37:37浏览次数:48  
标签:容器 obj name STL age 元素 Student



文章目录

  • 一、STL 容器的 值 ( Value ) 语意
  • 1、STL 容器存储任意类型元素原理
  • 2、STL 容器元素可拷贝原理
  • 3、STL 容器元素类型需要满足的要求
  • 4、STL 容器迭代器遍历
  • 二、代码示例 - 自定义可存放入 STL 容器的元素类
  • 1、代码示例
  • 2、执行结果







一、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 容器外 , 都可以使用如下代码进行遍历 ;

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






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



1、代码示例



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

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


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

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



在 有参构造 函数中 , 主要作用是 创建新对象

/// <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 对象 初始化新对象

/// <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 类型的成员直接赋值 ;

/// <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* 内存 , 然后置空 ;

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



代码示例 :

// 调用 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 . . .

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


标签:容器,obj,name,STL,age,元素,Student
From: https://blog.51cto.com/u_14202100/9145447

相关文章

  • 【C++】STL 算法 ② ( foreach 循环中传入 函数对象 / Lambda 表达式处理元素 | forea
    文章目录一、foreach循环中传入函数对象/Lambda表达式处理元素1、foreach循环算法2、foreach循环中传入函数对象处理元素3、foreach循环中传入Lambda表达式处理元素4、Lambda表达式-匿名函数对象/仿函数一、foreach循环中传入函数对象/Lambda表达式处理......
  • 【C++】STL 容器 - map 关联容器 ④ ( map 容器常用 api 操作 | 查找指定元素 | 获取
    文章目录一、查找指定元素-std::map#find()函数1、函数原型简介2、代码示例二、获取元素个数-std::map#count()函数1、函数原型简介2、代码示例三、获取大于等于指定键的元素-std::map#lower_bound函数1、函数原型简介2、代码示例四、获取大于指定键的元素-std::map#up......
  • 元素显示与隐藏
    引入大家注意这个效果广告按钮,当我点击那个❌的时候,我们会发现广告自己会消失不见并且不会占有原来的位置。这里就要用到一个css的知识,隐藏displaydisplay属性用于设置一个元素应如何显示。none:元素不会被显示。block:元素显示为块级元素,像<p>、<div>这样的标签默认就是块级元......
  • Kubernetes容器实践深度解析
    Kubernetes容器实践深度解析引言在当今云原生时代,容器技术已经成为构建、部署和管理应用程序的关键工具之一。而在众多的容器编排系统中,Kubernetes(简称K8s)因其强大的自动化、弹性和可扩展性而备受欢迎。本文将深入探讨Kubernetes容器实践,从基础概念到高级应用,为读者提供全面的指南......
  • list容器&迭代器应用
     1.#include<iostream>#include<list>usingnamespacestd;intmain(){ list<int>a={1,2,3,4,5}; list<int>b={6,7,8,9,10}; list<int>::iteratori; a.splice(a.end(),b,b.begin(),b.end()); for(i=a.begin();i!=a.end();++i){ ......
  • 请将鼠标悬停在下面的元素上,即可查看 2D 和 3D 转换之间的区别
    请将鼠标悬停在下面的元素上,即可查看2D和3D转换之间的区别:2Drotate3Drotatetransformcode<div><p>请将鼠标悬停在下面的元素上,即可查看2D和3D转换之间的区别:</p><style>#rotate2D,#rotate3D{width:80px;height:70px;color:white;......
  • 回溯法求解n个元素的集合的幂集
      过程:树中的根节点表示幂集元素的初始状态(为空集);叶子节点表示它的终结状态中幂集ρ(A)的8个元素;第i层(i=1,2,3,...,n)层的分支节点,则表示已对集合A中前i-1个元素进行了取/舍处理的当前状态(其中左分支表示“取”,右分支表示“舍”);将上述问题求解集合的幂集转换为先序遍历这棵......
  • 通过模板类实现一个简单的vector容器
    什么是模板模板分为类模板和函数模板,关键字为template,基本的声明形式如下:template<classT>;//也可以写成这样template<typenameT>class和typename在声明模板参数时的用法是相似的,一般情况下可以互换但在成员模板内部访问嵌套类型时,需要使用typename。下面举一个例子加以理......
  • 以Docker容器的形式运行GVM-11
    以Docker容器的形式运行GVM-11OpenVAS(OpenVulnerabilityAssessmentSystem)是在nessus基础上发展起来的一个开源的漏洞扫描程序,其核心部件是一套漏洞测试程序,可以检测远程系统和应用程序中的安全问题。 (一)在ubuntu18系统中安装dockeraptinstalldocker.io //在线安装dockersy......
  • 11、盛水最多的容器
    法一:暴力解法(超时)intmaxArea(vector<int>&height){intmax=0;for(inti=0;i<height.size();i++){for(intj=i+1;j<height.size();j++){intminHeight=min(height[i],height[j]);intcapacity=(j-i......