Lesson 08 string类 (中)

news2024/12/24 9:08:09

C++:渴望力量吗,少年?

文章目录

  • 二、string类的介绍与使用
    • 2. 使用
        • (5)string类对象的修改操作
  • 三、拷贝
    • 1. 引入
    • 2. 浅拷贝
    • 3. 深拷贝
  • 总结


二、string类的介绍与使用

2. 使用

(5)string类对象的修改操作
函数名称功能说明
push_back在字符串后尾插字符
append在字符串后追加一个字符串
operator+= (重点)在字符串后追加字符串str
c_str(重点)返回C格式字符串
find + npos(重点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回

注意:
  a. 在string尾部追加字符时,s.push_back(c ) / s.append(1, c) / s += ‘c’ 三种的实现方式差不多,一般情况下string类的+=操作用得比较多,+=操作不仅可以连接单个字符,还可以连接字符串
  b. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

void test_string6()
{
    cout << "我是test_string6 :" << endl;
    string s;
    //s.reserve(100);//保留至少100个字节的空间,如果提前知道所需的空间,可以提前开好,因为扩容需要代价
    //容器的容量不一定就是我们开的那个,可能大于这个数字

    size_t old = s.capacity();//Return size of allocated storage(返回已分配的空间大小)
    cout << "初始" << s.capacity() << endl;

    for (size_t i = 0; i < 100; i++)//观察扩容的情况
    {
        s.push_back('x');

        if (s.capacity() != old)
        {
            cout << "此时i = " << i << ", 扩容:" << s.capacity() << endl;
            old = s.capacity();
        }
    }

    s.reserve(10);//string可以自由地优化,并使字符串的容量大于 n。
    cout << s.capacity() << endl;
}

void test_string7()//考虑现有的size和capacity的关系
{
    cout << "我是test_string7 :" << endl;
    string s1("hello world");
    cout << s1 << endl;
    cout << s1.size() << endl;//s.size() == s.length(),这两个都不包括斜杠零
    cout << s1.capacity() << endl;

    //s1.resize(13);//多出来的空间放置斜杠零
    //s1.resize(13, 'x');//已有数据个数小于resize的个数,相当于插入新的数据
    s1.resize(20, 'x');//如果resize开的空间大于最开始的capacity也就是15,就会自动扩容
    cout << s1 << endl;
    cout << s1.size() << endl;
    cout << s1.capacity() << endl;

    s1.resize(5);//已有数据的空间大于resize的空间,相当于删除数据
    cout << s1 << endl;
    cout << s1.size() << endl;
    cout << s1.capacity() << endl;

    string s2;
    s2.resize(10, '#');//字符串没有数据/没有初始化,相当于直接填充数据
    cout << s2 << endl;
    cout << s2.size() << endl;
    cout << s2.capacity() << endl;

    s2[0]++;
    s2.at(0)++;//Return value:The character at the specified position in the string.(返回特定位置的字符)
    cout << s2 << endl;
}

void test_string8()
{
    cout << "我是test_string8 :" << endl;
    string ss("world");

    string s;
    s.push_back('#');
    s.append("hello");//在对象s的结尾追加,和pushback、+=的效果差不多,一般使用+=多一些
    s.append(ss);
    cout << s << endl;

    s += '#';
    s += "hello";
    s += ss;
    cout << s << endl;

    string ret1 = ss + '#';//尽量使用 +=,因为 + 的拷贝构造次数会更多,影响效率
    string ret2 = ss + "hello";
    cout << ret1 << endl;
    cout << ret2 << endl;
}

void test_string9()
{
    cout << "我是test_string9 :" << endl;
    std::string str("xxxxxxx");
    std::string base = "The quick brown fox jumps over a lazy dog.";

    //str = base;//可以直接用 = 赋值
    //cout << str << endl;

    str.assign(base);//相当于base赋值给str,会覆盖原来的值
    std::cout << str << '\n';

    str.assign(base, 5, 10);//从base下标为5的位置开始将10个字符赋值给str
    std::cout << str << '\n';

    //str.assign(base.begin()+16,base.end()-12);//参数也可以是迭代器
}

void test_string10()
{
    cout << "我是test_string10 :" << endl;
    // insert/erase/repalce能不用就尽量不用,因为他们都涉及挪动数据,效率不高
    // 接口设计复杂繁多,需要时查一下文档即可
    std::string str("hello world");
    str.insert(0, 1, 'x');//在下标为第一个参数的位置插入个数为第二个参数的第三个参数
    str.insert(str.begin(), 'x');
    cout << str << endl;

    str.erase(5);//从下标为5的位置开始擦除内容,第二个参数是缺省参数,不写默认擦除第一个参数后面的所有内容
    cout << str << endl;

    std::string s1("hello world");
    s1.replace(5, 3, "%%20");//将从指定位置开始的指定个数字符替换为指定字符(串)
    //3:Number of characters to replace (if the string is shorter, as many characters as possible are replaced).
    cout << s1 << endl;

    // 要求将空格替换为20%(以空间换时间)(replace效率不够高)
    std::string s2("The quick brown fox jumps over a lazy dog.");
    string s3;
    for (auto ch : s2)
    {
        if (ch != ' ')
        {
            s3 += ch;
        }
        else
        {
            s3 += "20%";
        }
    }

    //s2 = s3;
    //s2.assign(s3);
    printf("s2:%p\n", s2.c_str());//c_str可以返回这个对象的指针
    printf("s3:%p\n", s3.c_str());

    //交换的目的是让s2的内容变成修改之后的内容
    swap(s2, s3);//注意有swap模板库,也有全局函数swap,还有string自己提供的swap,如果用的话一般自定义类型不要用模板的swap,因为要拷贝构造的次数比较多
    //不过一般直接调用swap交换字符串也不会调用模板库的,因为匹配的模板和匹配的已经实现的函数之间编译器会优先使用已经实现的函数
    //s2.swap(s3);

    printf("s2:%p\n", s2.c_str());
    printf("s3:%p\n", s3.c_str());

    cout << s2 << endl;
}

void test_string11()
{
    cout << "我是test_string11 :" << endl;
    string s1("test.cpp.tar.zip");
    //返回找到的下标
    //size_t i = s1.find('.');//正向查找第一个 . 
    size_t i = s1.rfind('.');//反向查找第一个 .
    cout << i << endl;

    string s2 = s1.substr(i, 3);//从下标为i的位置开始取子串,第二个参数没写就取剩下的全部
    cout << s2 << endl;

    //string s3("https://legacy.cplusplus.com/reference/string/string/rfind/");
    string s3("ftp://www.baidu.com/?tn=65081411_1_oem_dg");
    // 协议
    // 域名
    // 资源名

    string sub1, sub2, sub3;
    size_t i1 = s3.find(':');
    if (i1 != string::npos)//因为找不到对应的字符是返回npos
        sub1 = s3.substr(0, i1);//注意是左闭右开的区间,substr返回一个子串
    else
        cout << "没有找到i1" << endl;

    size_t i2 = s3.find('/', i1 + 3);
    if (i2 != string::npos)
        sub2 = s3.substr(i1 + 3, i2 - (i1 + 3));
    else
        cout << "没有找到i2" << endl;

    sub3 = s3.substr(i2 + 1);//取到末尾

    cout << sub1 << endl;
    cout << sub2 << endl;
    cout << sub3 << endl;
}

void test_string12()
{
    cout << "我是test_string12 :" << endl;
    /*std::string str("Please, replace the vowels in this sentence by asterisks.");
    std::size_t found = str.find_first_not_of("abc");//找到字符串第一个不是abc的字符并返回下标
    while (found != std::string::npos)
    {
        str[found] = '*';
        found = str.find_first_not_of("abcdefg", found + 1);//第二个参数:Position of the first character in the string to be considered in the search.
    }

    std::cout << str << '\n';*/

    std::string str("Please, replace the vowels in this sentence by asterisks.");
    std::size_t found = str.find_first_of("abcd");//找到第一个是abcd中其中一个字符的并返回下标
    while (found != std::string::npos)
    {
        str[found] = '*';
        found = str.find_first_of("abcd", found + 1);
    }

    std::cout << str << '\n';

}

int main()
{
    test_string6();
    test_string7();
    test_string8();
    test_string9();
    test_string10();
    test_string11();
    test_string12();

    return 0;
}

  string类中还有一些其他的操作,这里不一一列举,大家在需要用到时查文档即可。

三、拷贝

1. 引入

  上面已经对string类进行了简单的介绍,其他不太常用的函数就不再写了,只要能够保证上面的内容都能熟练运用即可。为什么模拟实现string类?模拟string类最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数,只有能够模拟出来,才是真正了解它的底层逻辑。
  问题:以下string类的实现是否有错?
代码如下:

class String {
public:
	String(const char* str = "")
	{
		if (nullptr == str)
		{
			assert(false);
			return;
		}

		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};

// 测试
void TestString()
{
	String s1("hello bit!!!");
	String s2(s1);
}

  这段代码其实是有很大的问题的,这个关于拷贝的问题也是我们学习C++经常会遇到的问题,所以还是得理解为什么。
在这里插入图片描述
  上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

2. 浅拷贝

  浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中存在管理的资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

3. 深拷贝

  如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
  上面的代码可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不和其他对象共享。
在这里插入图片描述
  如图,显式地定义拷贝构造函数,在初始化列表重新为_pStr开辟一个新的空间,再把数据从就空间拷贝过去,这样就实现了深拷贝。


总结

  前面这两小节主要还是为了下一节地模拟实现做准备,要尽可能做到理解并熟练运用。

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

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

相关文章

第一百八十五回 如何禁止页面跟随手机自动旋转

文章目录 1. 概念介绍2. 使用方法2.1 全面禁止2.2 局部禁止3. 示例代码4. 内容总结我们在上一章回中介绍了"如何自定义Radio组件"相关的内容,本章回中将介绍 如何禁止页面随手机自动旋转.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 在手机默认设置下,手机…

CentOS7.5搭建Hadoop-3.3.6集群的详细操作流程-实操版本

一、准备工作 1、安装 VMware&#xff0c;已安装的&#xff0c;跳过此步骤即可 官方正版VMware下载&#xff08;16 pro&#xff09;&#xff1a;https://www.aliyundrive.com/s/wF66w8kW9ac 安装&#xff1a;选一下安装地址&#xff0c;一直下一步即可。&#xff08;可能会要…

QListWidget中自定义widget大小自适应

背景&#xff1a; QListWidget中的item&#xff0c;可以添加自定义的widget。 但是怎么去调整widget的大小呢&#xff1f; 参考&#xff1a;QT QListWidget的添加与删除&#xff0c;滚动条显示或隐藏&#xff0c;判断是否滑到顶部或底部&#xff0c;并使QListWidgetItem自适…

.Net 8 Blazor下 Auto交互渲染模式试用

一、环境 C:\Users\zhuji>dotnet --version 8.0.100C:\Users\zhuji>dotnet --list-sdks 5.0.403 [C:\Program Files\dotnet\sdk] 6.0.404 [C:\Program Files\dotnet\sdk] 8.0.100 [C:\Program Files\dotnet\sdk] Microsoft Visual Studio Enterprise 2022 (64 位) - Cu…

SQL Sever 基础知识 - 数据查询

SQL Sever 基础知识 - 一、查询数据 一、查询数据第1节 基本 SQL Server 语句SELECT第2节 SELECT语句示例2.1 SELECT - 检索表示例的某些列2.2 SELECT - 检索表的所有列2.3 SELECT - 对结果集进行筛选2.4 SELECT - 对结果集进行排序2.5 SELECT - 对结果集进行分组2.5 SELECT - …

数学建模-基于集成学习的共享单车异常检测的研究

基于集成学习的共享单车异常检测的研究 整体求解过程概述(摘要) 近年来&#xff0c;共享单车的快速发展在方便了人们出行的同时&#xff0c;也对城市交通产生了一定的负面影响&#xff0c;其主要原因为单车资源配置的不合理。本文通过建立单车租赁数量的预测模型和异常检测模型…

IDEA中springboot 提示 java: 找不到符号 符号: 变量 log

在以下位置加上该配置"-Djps.track.ap.dependenciesfalse" 然后重新启动项目&#xff0c;到此问题解决&#xff01;&#xff01;&#xff01;

软件工程 课后题 选择 查缺补漏

在一张状态图中只能有一个初态&#xff0c;而终态则可以没有&#xff0c;也可以有多个 所有的对象可以成为各种对象类&#xff0c;每个对象类都定义了一组 方法 通过执行对象的操作可以改变对象的属性&#xff0c;但它必须经过 消息 的传递 UML应用于 基于对象的面向对象的方…

js事件流与事件委托/事件代理

1 事件流 事件流分为两步&#xff0c;一是捕获&#xff0c;二是冒泡 1.1 捕获概念 捕获就是从最高层一层一层往下找到最内部的节点 1.2 冒泡概念 捕获到最小节点后&#xff0c;一层一层往上返回&#xff0c;像是气泡从最底部往上冒一样&#xff0c;由于水深不同压强不同&…

Git分支批量清理利器:自定义命令行插件实战

说在前面 不知道大家平时工作的时候会不会需要经常新建git分支来开发新需求呢&#xff1f;在我这边工作的时候&#xff0c;需求都是以issue的形式来进行开发&#xff0c;每个issue新建一个关联的分支来进行开发&#xff0c;这样可以通过issue看到一个需求完整的开发记录&#x…

Mybatis 的简单运用介绍

Mybatis 用于操作数据库 操作数据库肯定需要: 1.SQL语句 2.数据库对象和 java 对象的映射 接下来我们看看怎么使用 Mybatis 我们先搞一些数据库内容 然后将其这些内容和Java对象进行映射 再创建一个类实现 select * from 再写一个类证明上述代码是否可以实现 别忘了在appli…

vue3+element-plus+vue-cropper实现裁剪图片上传

1.vue3element-plusvue-cropper实现裁剪图片 element-UI官网element-plus官网vue-croppervue3使用vue-cropper安装&#xff1a;npm install vue-croppernext 2.vue-cropper插件&#xff1a; <vue-cropper :img"option.img" /><script setup>import {reac…

组合(回溯算法)

77. 组合 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 样例输入 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],…

CSP认证2023-03:田地丈量、垦田计划、LDAP,python满分解答代码

CSP认证2023-03&#xff1a;田地丈量、垦田计划、LDAP&#xff0c;python满分解答代码 目录 一、田地丈量 问题描述 输入输出 思路 代码和结果 二、垦田计划 问题描述 输入和输出 思路 代码和结果 三、LDAP 问题描述 思路 代码和结果 一、田地丈量 问题描…

在ubuntu虚拟机上安装不同版本的交叉编译工具链

在之前的章节中&#xff0c;学习了如何安装了4.8.3的交叉编译工具链&#xff1a; 交叉编译 和 软硬链接 的初识&#xff08;面试重点&#xff09;-CSDN博客 但是&#xff0c;在之后学习内核编译时&#xff0c;由于我的树莓派内核版本较高&#xff0c;为6.1&#xff0c;所以在…

贪心 55. 跳跃游戏 45.跳跃游戏 II

55. 跳跃游戏 题目&#xff1a; 给定非负数组&#xff0c;初始位置在数组第一格&#xff0c;数组值是可以选择的最大跳跃步数&#xff0c;判断能不能达到数组末尾。 示例 1: * 输入: [2,3,1,1,4] * 输出: true * 解释: 我们可以先跳 1 步&#xff0c;从位置 0 到达 位置 1,…

【多传感器融合】BEVFusion: 激光雷达和视觉融合框架 NeurIPS 2022

前言 BEVFusion其实有两篇&#xff0c; 【1】BEVFusion: A Simple and Robust LiDAR-Camera Fusion Framework. NeurIPS 2022 | 北大&阿里提出 【2】BEVFusion: Multi-Task Multi-Sensor Fusion with Unified Bird’s-Eye View Representation 2022 | MIT提出 本文先分…

VUE2+THREE.JS点击事件

THREE.JS点击事件 1.增加监听点击事件2.点击事件实现3.记得关闭页面时 销毁此监听事件 1.增加监听点击事件 renderer.domElement.addEventListener("click", this.onClick, false); 注:初始化render时监听 2.点击事件实现 onClick(event) {const raycaster new …

leetcode-142-环形链表(C语言实现)

题目&#xff1a; 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评…

药食同源的食物哪些适合冬季吃?

药食同源的食物是指在中医理论指导下&#xff0c;既是药物又是食物的天然植物和动物&#xff0c;具有营养和药效双重作用。在冬季&#xff0c;由于气候寒冷&#xff0c;人体需要更多的热量和营养来保持温暖和健康&#xff0c;因此药食同源的食物在冬季特别适合食用。以下是几种…