string模拟优化和vector使用

 1.简单介绍编码

utf_8变长编码,常用英文字母使用1个字节,对于其它语言可能2到14,大部分编码是utf_8,char_16是编码为utf_16,     char_32是编码为utf_32,   wchar_t是宽字符的,

utf_16是大小为俩个字节,所以牛马还有一个'\0'一个是3*2六个字节

utf_32是大小为四个字节所以大小就是3*4十二个字节

wchar_t 在window上通常为2字节,其它环境有不同大小。

	char16_t str16[] = u"牛马";
	char32_t str32[] = U"牛马";
	wchar_t wstr[] = L"牛马";

	cout << sizeof(str) << endl;
	cout << sizeof(str16) << endl;
	cout << sizeof(str32) << endl;
	cout << sizeof(wstr) << endl;*/

 2.对于拷贝优化

省略的部分是可以用上面的代替,先构造一个tmp出来,然后把s1复制到tmp上,再去交换s2和tmp,这里是浅拷贝了指针,如果不用交换而是直接把指针值复制过去会有野指针的问题,因为另一个被复制的变量如果被销毁了,那么它指向的空间也会被收回,浅拷贝是一个一个字节复制的,所以此时还剩一个变量指向被释放的空间就会有问题,而swap可以很好的解决,交换完后tmp销毁了,但是销毁的是s1的空间。

void test_string6()
	{
		string s1("hello world");
		string s2 = s1;

		cout << s1 << endl;
		cout << s2 << endl;

		string s3("xxxxxxxxxxxxxx");
		s1 = s3;

		cout << s1 << endl;
		cout << s3 << endl;
	}
	string(const char* str = "")
		{
			_size = strlen(str);
			// _capacity不包含\0
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

string(const string& s)
		{
			string tmp(s._str);
			swap(tmp);
		}


	// 深拷贝问题
		// s2(s1)
		/*string(const string& s)
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}*/

	void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

 

 

3.赋值运算符重载

先判断是否相等,是则直接结束,不一样就构造tmp并拷贝,然后交换,这里的if除了判断是否一样还有其它作用,{}符号表明了一个局部作用域,所以局部变量在出了这个{}会被销毁,tmp就会被销毁,一举两得。

	 //s1 = s3;
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				//string tmp(s._str);
				string tmp(s);

				swap(tmp);
			}

			return *this;
		}

下面是进一步优化

在形参数部分变为string tmp,这样s3作为参数时,先构造tmp然后指向拷贝构造,这里swap只写tmp是因为还有一个隐式参数*this,所以交换,除了{}tmp销毁,代码简洁性高。

	// s1 = s3;
		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

 

 4.swap

可以看到模板的swap是先拷贝c,然后在赋值运算符重载俩次,注意如果a和b都有指向资源,那么就要用深拷贝,那么一次swap就三次深拷贝,是非常占资源的,所以用上面的就相对效率高,如果字节的类里面有,这里的std::swap调用的却是全局的swap,全局的swap效率高于模板swap(std::swap),而且有实例化的swap也不会去模板生成swap。是因为编译器会去找最适配的swap,就像你像吃辣条,你的妈妈却还是给你买健康的食品。

 5.简单介绍引用计数和写时拷贝

引用计数:在加一个变量来记录有多少个指针指向这个空间,这样就有判断的标准,如果大于1就是有多个指向这块空间,等于一就说明只有一个指针指向这里,可以自由修改,也不会有多次析构。

写时拷贝:

写时拷贝是一种优化技术,通常用于减少内存使用和提高性能。它允许多个对象共享同一份数据,直到有一个对象需要修改这份数据为止。只有在需要修改时,才会进行数据的实际拷贝,从而避免不必要的内存分配和复制操作

6.vector简单使用

 1.创建vector

创建俩个vector变量,vector是stl的模板要显式实例化,可以看到不写参数是走缺省参数的,而10,1就是10个int的元素,值为1。

用reserve(域string的作用一样,提前开空间),可以看到比原来大会改变,再改小则不会变化。

	vector<int> v2(10, 1);
	v2.reserve(15);
	cout << v2.size() << endl;
	cout << v2.capacity() << endl;


	v2.reserve(25);
	cout << v2.size() << endl;
	cout << v2.capacity() << endl;


	v2.reserve(5);
	cout << v2.size() << endl;
	cout << v2.capacity() << endl;

2.使用resize

resize函数会改变vector实例对象的大小,当为5时就只有五个在vector里面,

 

这里是如果扩大则会填充第二个参数,一开始是10个1,后面变成15,则再填5个2,25就再填10个3. 

void test_vector3()
{
	//TestVectorExpand();

	vector<int> v(10, 1);
	v.reserve(20);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(15, 2);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(25, 3);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	v.resize(5);
	cout << v.size() << endl;
	cout << v.capacity() << endl;
}

 

 

 

3.二维的vector 

先初始化一个一维的v,然后以v为参数构造二维的vv,


	vector<int> v(10, 1);
	vector<vector<int>> vv(5,v);

 

4.迭代器和范围for遍历vector

这里前置++和前置--则最后只有八位打印出来,因为是从begin后一位和end前一位,循环和范围for也可以逐个打印,像之前的数组一样。 

 

void test_vector1()
{
	vector<int> v1;
	vector<int> v2(10, 1);

	vector<int> v3(++v2.begin(), --v2.end());

	for (size_t i = 0; i < v3.size(); i++)
	{
		cout << v3[i] << " ";
	}
	cout << endl;

	vector<int>::iterator it = v3.begin();
	while (it != v3.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	for (auto e : v3)
	{
		cout << e << " ";
	}
	cout << endl;
}

 

 5.vector的操作

insert是在vector中插入数据,前一个是位置,后一个是插入什么元素,后面用for来cin初始化vector。

	vector<int> v(10, 1);
	v.push_back(2);
	v.insert(v.begin(), 0);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	v.insert(v.begin() + 3, 10);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	vector<int> v1(5, 0);
	for (size_t i = 0; i < 5; i++)
	{
		cin >> v1[i];
	}

	for (auto e : v1)
	{
		cout << e << ",";
	}
	cout << endl;

 

前面知识补充:

在 C++ 中,可以使用强制转换将指针类型转换为 void*,这是合法的,且在某些情况下非常有用。下面,我将详细解释如何在 C++ 中使用 void*,以及在与 std::cout 结合使用时的注意事项。
1. 强制转换为 void*
在 C++ 中,任何指针类型都可以被隐式或显式转换为 void*。void* 是一种通用指针类型,能够指向任何类型的数据。通过这种转换,你可以在不知道指针具体类型的情况下处理内存地址。这在处理底层内存操作或需要泛型编程的情况下特别有用。
2. 与 std::cout 的结合使用
如你所述,std::cout 对 const char* 类型进行了特别的处理,重载了 &lt;&lt; 运算符以打印字符串的内容。如果你需要打印 const char* 的地址,可以使用 (void*) 来强制转换:
std::cout &lt;&lt; (void*)cStr;

这将输出指针的内存地址,而不是它指向的字符串内容。
3. 示例代码
以下是一个完整的示例代码,演示了如何使用 void* 强制转换以及打印字符串内容和指针地址的区别:
#include &lt;iostream&gt;
#include &lt;string&gt;

int main() {
    std::string str = "Hello, World!";
    const char* cStr = str.c_str();

    // 输出字符串内容
    std::cout &lt;&lt; "内容: " &lt;&lt; cStr &lt;&lt; std::endl;  // 输出: Hello, World!

    // 输出指针的地址
    std::cout &lt;&lt; "指针地址: " &lt;&lt; (void*)cStr &lt;&lt; std::endl;  // 输出: 指针的内存地址

    return 0;
}

4. 输出结果
运行上述代码时,输出将类似于:
内容: Hello, World!
指针地址: 0x7ffee6b7c3d0  // 这个值会因每次运行而不同

5. 关键点总结

1.重载运算符:std::cout 对 const char* 类型的重载实现允许直接输出字符串内容。
2.地址输出:如果你想输出指针的地址,必须使用 (void*) 来进行强制转换。
3.合法性:使用强制转换 void* 是合法的,可以用于指针类型与通用指针之间的转换。

6. 注意事项
虽然强制转换为 void* 是合法的,但在某些情况下需要谨慎使用:

4.类型安全:使用 void* 可能会导致类型安全问题,尤其是在进行指针运算时。
5.需要显式转换回原始类型:在使用 void* 时,如果需要再次使用原始类型的指针,你必须显式地将其转换回来,这可能会引入错误。

总的来说,使用 void* 是 C++ 中一种灵活的指针处理方式,但要注意相应的安全性和可读性问题。

在 C++ 中,std::string 类提供了 replace 函数,用于替换字符串中的部分内容。这个函数可以使用不同的参数形式,允许你根据需求替换子字符串或单个字符。
std::string::replace 函数的基本用法
1. 替换子字符串
使用 std::string::replace 来替换指定范围内的字符,可以按以下方式调用:
std::string&amp; replace(size_t pos, size_t len, const std::string&amp; str);


1.pos:要替换的起始位置。
2.len:要替换的字符数。
3.str:用于替换的字符串。

示例代码:
#include &lt;iostream&gt;
#include &lt;string&gt;

int main() {
    std::string str = "Hello, world!";

    // 从位置 7 开始替换 5 个字符
    str.replace(7, 5, "C++");

    std::cout &lt;&lt; str &lt;&lt; std::endl; // 输出: Hello, C++!
    return 0;
}

2. 替换单个字符
std::string::replace 也可以用来替换单个字符,示例如下:
std::string&amp; replace(size_t pos, size_t len, size_t n, char c);


4.n:替换字符的数量。
5.c:要替换成的字符。

示例代码:
#include &lt;iostream&gt;
#include &lt;string&gt;

int main() {
    std::string str = "Hello, world!";

    // 从位置 7 开始替换 5 个字符为字符 '*'
    str.replace(7, 5, 3, '*');

    std::cout &lt;&lt; str &lt;&lt; std::endl; // 输出: Hello, ***d!
    return 0;
}

3. 替换另一个字符串
你还可以用另一个字符串的子串来替换当前字符串中的部分内容:
std::string&amp; replace(size_t pos, size_t len, const char* s);

示例代码:
#include &lt;iostream&gt;
#include &lt;string&gt;

int main() {
    std::string str = "Hello, world!";

    // 从位置 7 开始替换 5 个字符为 C 风格字符串 "C++"
    str.replace(7, 5, "C++");

    std::cout &lt;&lt; str &lt;&lt; std::endl; // 输出: Hello, C++!
    return 0;
}

4. 替换多个字符
如果需要替换多个字符,可以使用 std::string::replace 结合字符串的长度来实现:
#include &lt;iostream&gt;
#include &lt;string&gt;

int main() {
    std::string str = "Hello, world! Hello, world!";

    // 替换所有 "world" 为 "C++"
    size_t pos = 0;
    std::string to_replace = "world";
    std::string replace_with = "C++";

    while ((pos = str.find(to_replace, pos)) != std::string::npos) {
        str.replace(pos, to_replace.length(), replace_with);
        pos += replace_with.length(); // Move past the replaced part
    }

    std::cout &lt;&lt; str &lt;&lt; std::endl; // 输出: Hello, C++! Hello, C++!
    return 0;
}

总结
std::string::replace 是一个强大的工具,可以灵活地对字符串中的部分内容进行替换。根据需要,可以选择替换字符、字符串或使用 C 风格字符串。通过适当的循环和查找,可以实现更复杂的替换操作。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

玩转大模型(二)启动一个大模型

写这篇文章是几个月前了&#xff0c;现在模型和框架已经更新了好几轮了&#xff0c;但不影响吧&#xff0c;后面会把这几个月的发展专门用一篇文章补上。 在上一篇中&#xff0c;硬件全都准备好了&#xff0c;软件也已经安装完成&#xff0c;相应的配置都已经做完&#xff0c;…

StarRocks大批量数据导入方案-使用 Kafka connector 导入数据

本文详细介绍如何使用Routine Load 导入数据 一、准备工作 1.1 安装基础环境 主要是安装StarRocks和Kafka&#xff0c;本文直接跳过不做详细介绍~ 二、概念及原理 2.1 概念 导入作业&#xff08;Load job&#xff09; 导入作业会常驻运行&#xff0c;当导入作业的状态为 R…

使用idea和vecode创建vue项目并启动(超详细)

一、idea创建vue项目 创建项目之前先下载好插件 新建项目找到vue生成器 写好名称&#xff0c;找到自己需要存放的地址&#xff0c;node解释器安装方式可以看我上一个博客&#xff0c;vueCLI是选择vue的版本&#xff0c;我们可以使用idea自带的vue版本默认是vue3&#xff0c;创…

SQL注入漏洞(三)

报错注入 group by重复键冲突 &#xff08;count()floor()rand()group by 组合&#xff09;就是利用 count()、rand()、floor()、 group by 这几个特定的函数结合在一起产生的注入漏洞。 ?id1 and (select 1 from (select count(*),concat(0x5e,(select version() from inf…

[含文档+PPT+源码等]精品基于Nodejs实现的微信小程序校园心理健康平台设计与实现

《[含文档PPT源码等]精品基于Nodejs实现的微信小程序校园心理健康平台设计与实现》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等福利&#xff01; 软件开发环境及开发工具&#xff1a; 操作系统&#xff1a;Windows 10、Windows 7、Windows 8 开…

前端excel的实现方案Luckysheet

一、介绍 Luckysheet是一款纯前端类似excel的在线表格&#xff0c;功能强大、配置简单、完全开源的插件。目前已暂停维护&#xff0c;但是其已有功能大概能满足常见需求的使用。 二、引入 ①cdn引入&#xff08;目前应该已经不支持&#xff0c;可自行尝试&#xff09; <l…

mysql 10 单表访问方法

01.优化的过程 对于我们这些 MySQL 的使用者来说&#xff0c; MySQL 其实就是一个软件&#xff0c;平时用的最多的就是查询功能。DBA时不时丢过来一些慢查询语句让优化&#xff0c;我们如果连查询是怎么执行的都不清楚还优化个毛线&#xff0c;所以是时候掌握真正的技术了。我…

LED显示屏与手机连接:简单便捷的操作指南

随着科技的飞速发展&#xff0c;LED显示屏已经成为现代商业和公共信息展示的重要工具。它们不仅在户外广告中扮演着重要角色&#xff0c;室内应用也越来越广泛。智能手机的普及使得我们希望能够通过手机直接控制LED显示屏&#xff0c;以实现更灵活、更便捷的信息展示。那么&…

证件照小程序源码,前后端稳定运行

演示&#xff1a;证寸照制作 运行环境: Linux Nginx PHP >5.6 MySQL>5.6 安装步骤: 1.下载源码上传至你的服务器宝塔面板 2.直接添加站点选择源码目录&#xff0c;新建数据库 3.设置代码执行目录为/web 4.在浏览器中输入你的域名&#xff0c;会提示安装&#xff0c;填写…

5 -《本地部署开源大模型》在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战

在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战 无论是在单机单卡&#xff08;一台机器上只有一块GPU&#xff09;还是单机多卡&#xff08;一台机器上有多块GPU&#xff09;的硬件配置上启动ChatGLM3-6B模型&#xff0c;其前置环境配置和项目文件是相同的。如果大家对配置过程还…

Django的模板语法

Django的模板语法 1、初步认识2、原理 1、初步认识 本质上&#xff1a;在HTML中写一些占位符&#xff0c;由数据对这些占位符进行替换和处理。 在views.py中用字典&#xff08;键值对&#xff09;的形式传参&#xff0c;在html文件中用两个花括号来显示单独的值 列表、元组等数…

【UI】将 naive ui 的 message 封装进axios 中,关于naiveui的message相关的用法

文章目录 前言在setup外进行使用直接包裹使用vue 单文件中使用 参考文章&#xff1a; 关于naiveui的message相关的用法 前言 最近新建了一个vite vu3 的项目&#xff0c;完全是从0 到1 &#xff0c;封装到request 的时候 想对axios 请求做一个全局的处理&#xff0c;但发现…

dart-sass和node-sass的区别,使用dart-sass后可能会出现的问题

前言&#xff1a; 2020 年 10 月 27 日&#xff0c;Sass 官方团队正式宣布 Libsass 将弃用&#xff0c;以及基于它的 Node Sass 和 SassC&#xff0c;并且建议用户使用 Dart Sass。如果在 vue 脚手架搭建的项目中需要使用 sass&#xff0c;建议初始化时勾选 sass 配置&#xff…

扫雷(C 语言)

目录 一、游戏设计分析二、各个步骤的代码实现1. 游戏菜单界面的实现2. 游戏初始化3. 开始扫雷 三、完整代码四、总结 一、游戏设计分析 本次设计的扫雷游戏是展示一个 9 * 9 的棋盘&#xff0c;然后输入坐标进行判断&#xff0c;若是雷&#xff0c;则游戏结束&#xff0c;否则…

Redis拒绝连接问题分析与解决方案

目录 前言1. 问题描述2. Redis拒绝连接的常见原因分析2.1 Redis服务未启动2.2 Redis配置中的绑定地址问题2.3 防火墙或安全组问题2.4 Redis连接池耗尽2.5 Redis服务器负载过高2.6 权限配置问题 3. 深度解决方案和优化建议4. 总结 前言 在分布式系统中&#xff0c;Redis作为高性…

Unity之XR Interaction Toolkit 射线拖拽3DUI

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、想实现的功能二、实现原理1.UI挂在XRGrabInteractable、刚体、BoxCollder2.修改刚体属性3.加BoxCollder 总结 前言 VR项目里正常情况有放置两种3DUI的方式…

Git之代已修改文件的目录高亮设置

不管Android Studio或者Idea&#xff0c;进入Setting 选择如图所示&#xff0c;并进行勾选 就可以高亮了。

2023年10月自考《数据库系统原理》04735试题

目录 一.选择题 二.填空题 三.设计题 四.简答题 五.综合体 一.选择题 1.下列属于数据库中存储数据基本特点是 (书中)P28页 A.冗余大 B.无组织 C.不可共享 D.永久存储 2.浏览器/服务器结构描述不正确的是 (书中)P36页 A.三层客户/服务器结构 B.基于Web引用的客户/服务器…

AWD入门

一、简介 AWD(Attack With Defense&#xff0c;攻防兼备)模式。你需要在一场比赛里要扮演攻击方和防守方&#xff0c;攻者得分&#xff0c;失守者会被扣分。也就是说攻击别人的靶机可以获取 Flag 分数时&#xff0c;别人会被扣分&#xff0c;同时你也要保护自己的主机不被别人…

武汉正向科技|焦炉移动机车连锁控制系统的介绍

焦炉车辆连锁控制系统是采用格雷母线定位技术&#xff0c;无线数据传输技术以及计算机技术&#xff0c;实现推焦车、拦焦车、熄焦车、装煤车、导烟车的集中监控和系统管理&#xff0c;以及车间作业计划管理&#xff0c;作业实绩管理&#xff0c;作业联锁控制及安全控制等。 焦炉…