讨论字符串(未完)

news2024/12/30 3:33:50

讨论字符串

  • C语言中的字符串
  • C++中的字符串
    • `string`类的构造函数
    • `string`类对象的容量操作
    • `string`类对象元素访问及遍历
      • 迭代器
    • `string`类对象的修改操作
  • `string`类的模拟实现
    • 成员变量
    • 构造函数
    • 析构函数
    • 扩容/预留空间
    • `push_back`
    • 遍历
      • `operator[]`
      • 迭代器
    • `append`
    • 任意位置插入数据和删除数据
    • 查找元素
    • 拷贝构造

语言是由一句一句字符串组成的,编程中,我们想要输出一句话也少不了字符串的加入。

C语言中的字符串

C语言中没有字符串数据类型,但是存在字符串,使用""括起来的即字符串。字符串是以\0结尾的字符的集合。

// C语言中字符串的表现形式。
"abcd"; // 字符串常量
char str[] = "abcd";
char* strs = "abcd"; // str指向字符串首位置地址

字符串结束标志

C++中的字符串

C++标准库中的string类,就是字符串类。

string类文档

  • 根据文档介绍我们可以得知,字符串是表示字符序列的类。
  • string在底层实际是basic_string模板类的别名,typedef basic_string<char> string;
  • 由于string类在C++标准库中,因此在使用时需要使用std::string或者using namespace std;

string类的构造函数

string类的构造函数

string类的构造函数众多,可以通过字符串构造,可以通过一部分字符串构造,还可以使用迭代器区间构造。

#include <iostream>

using namespace std;

int main()
{
	string s; // 调用默认构造函数
	cout << s << endl;

	string s1("hello world");
	cout << s1 << endl;

	string s2("hello world", 2, 5);
	cout << s2 << endl;

	const char* str = "welcome";

	string s3(str);
	cout << s3 << endl;

	string s4(str, 2);
	cout << s4 << endl;

	string s5(10, 'x');
	cout << s5 << endl;

	return 0;
}

string构造函数结果

string类对象的容量操作

函数名功能
size返回字符串长度。
length返回字符串长度,和size作用一样。
capacity返回容器容量。
empty判断字符串是否为空。
clear清空字符串中数据。
reserve扩容/为字符串预留空间。
resize将有效字符个数设为n个,多出的空间用字符c填充。
max_size返回字符串最大长度。(基本没用)
shrink_to_fit缩容。(轻易不会选择缩容)

size()底层和length()完全相同,size()只是为了和其他容器接口保持一致。

int main()
{
	string s("hello world");
	cout << s.size() << endl;
	cout << s.length() << endl;
	return 0;
}

字符串长度

vs编译器下,string对象的初始capacity是15,以接近1.5倍的方式扩容。

int main()
{
	string s;
	int capacity = s.capacity();
	cout << s.capacity() << endl;
	for (int i = 0; i < 1000; i++)
	{
		int newCap = s.capacity();
		if (capacity != newCap)
		{
			cout << s.capacity() << endl;
			capacity = newCap;
		}
		s.push_back(i);
	}
	return 0;
}

扩容规则

在g++13编译器下,初始容量为15,后面以2倍方式扩容。

扩容

clear()清空内容,但不会影响容量。

int main()
{
	string s("hello worldxxxxxxxxxxx");
	cout << s.capacity() << endl;
	s.clear();
	cout << s.capacity() << endl;

	return 0;
}

clear清空数据

reserve(size_t n)可以指定字符串对象的初始容量,若n比初始容量小,则初始容量还是默认值。若比初始容量大,则字符串对象预先开出指定大小容量。

int main()
{
	string s;
	s.reserve(1);
	cout << s.capacity() << endl;

	s.reserve(100);
	cout << s.capacity() << endl;

	return 0;
}

reserve函数

resize(size_t n)/resize(size_t n, char c)n如果比现字符串长度小,则发生截断,若比现字符串长度长 ,则用字符c填充。

int main()
{
	string s1("hello world");
	s1.resize(4);
	cout << s1 << endl;

	string s2("no");
	s2.resize(10, '!');
	cout << s2 << endl;
	

	return 0;
}

resize函数

string类对象元素访问及遍历

函数名功能
operator[]返回pos位置的字符。
迭代器beginendbegin获取第一个字符的迭代器,end获取最后一个字符下一个位置的迭代器。
反向迭代器rbeginrend逆向遍历。
范围forC++11支持的语法,底层基于迭代器。

运算符重载operator[],也可以使用const修饰的对象使用。

void print_string(const string &s) {
    for (int i = 0; i < s.size(); ++i) {
        cout << s[i] << " ";
    }
    cout << endl;
}

int main() {
    string s("hello world");
    for (int i = 0; i < s.size(); ++i) {
        s[i] += 1;
        cout << s[i] << " ";
    }
    cout << endl;

    print_string(s);
    return 0;
}

operator[]

迭代器

begin迭代器

end迭代器

根据手册,begin迭代器返回指向第一个字符的位置,end迭代器返回指向末尾字符后面的位置。

int main() {
    string s("hello world");

    string::iterator it = s.begin();
    while (it != s.end()) {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}

迭代器遍历

C++11实现范围for遍历方式,底层基于迭代器。只要实现迭代器,就可以使用范围for

范围for遍历

string类对象的修改操作

函数名功能
push_back在字符串末尾处插入字符。
append在字符串末尾处插入字符串。
operator+=在字符串末尾处插入字符串。
c_str返回C语言格式字符串。
findpos位置开始查找字符,返回该位置下标。
rfind反向查找。
substrpos位置开始,截取n个字符,返回截取后的字符串。

find()的返回值,若没有找到字符,则返回string::nposstring::nposstring类的静态成员变量,值是-1。

int main() {
    string s("hello world");

    size_t pos = s.find('n');
    if (pos != string::npos) {
        cout << "找到了" << endl;
    } else {
        cout << "没找到" << endl;
    }
    return 0;
}

npos

string类的模拟实现

成员变量

模拟实现的string类的成员变量,由三部分组成,分别是存放字符串的字符指针、字符串长度变量以及字符串容量变量。

char *_str;
size_t _size;
size_t _capacity;

构造函数

编写构造函数时,需要思考深浅拷贝问题,如果是浅拷贝,则在调用析构函数时会出现问题。因此,成员变量_str一定要申请自己的空间,不能直接拷贝传参的地址。

string(const char *str = "")
        : _size(strlen(str))
        , _capacity(_size) {
    _str = new char[_capacity + 1]; // 进行深拷贝
    for (size_t i = 0; i < _size; i++) {
        _str[i] = str[i];
    }
}

析构函数

_str是通过动态开辟内存空间,因此在析构函数中需要释放空间。

~string() {
    delete[] _str;
    _str = nullptr;
    _size = _capacity = 0;
}

扩容/预留空间

传入数字n,如果n比容量_capacity大,则扩容。

void reserve(size_t n) {
    if(n > _capacity) {
        char* tmp = new char[n + 1];
        strcpy(tmp, _str); // 拷贝原有字符
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}

push_back

先需要判断容量是否满足条件,判断条件是_size == _capacity代表容量满了。

void push_back(char ch) {
    // 先判断容量
    if(_size == _capacity) {
        int newCapacity = _capacity == 0 ? 5 : 2 * _capacity;
        reserve(newCapacity);
    }
    _str[_size] = ch;
    _size++;
  	_str[_size] = '\0'; // 确保末尾位置是结束位
}

遍历

operator[]

只需要返回指定下标元素即可。

char &operator[](size_t pos) {
    return _str[pos];
}

const char &operator[](size_t pos) const {
    return _str[pos];
}

迭代器

此处迭代器采用指针方式,将char*作别名iterator

typedef char *iterator;
typedef const char *const_iterator;
  • 接下来只需要返回指定位置指针地址即可。
iterator begin() {
    return _str;
}

iterator end() {
    return _str + _size;
}

const_iterator begin() const {
    return _str;
}

const_iterator end() const {
    return _str + _size;
}
  • 只要迭代器实现,范围for就可以调用。

append

  • 首先还是需要考虑容量问题,但这次不是简单的扩容2倍了,需要按照传入的字符串来判断扩容容量。
string& append(const char* str) {
    int size = strlen(str);
    reserve(size + _size);
    strcpy(_str + _size, str);
    return *this;
}
  • 因此,operator+=可以直接复用。
string &operator+=(char ch) {
    push_back(ch);
    return *this;
}

string &operator+=(char *str) {
    append(str);
    return *this;
}

string &operator+=(const string &s) {
    int size = s._size;
    reserve(size + _size);
    strcpy(_str + _size, s._str);
    return *this;
}

任意位置插入数据和删除数据

  • 插入数据还是需要优先判断扩容,和前面差别不大,只是需要考虑数据挪动。
void insert(size_t pos, char ch) {
    assert(pos <= _size);
    if (_size == _capacity) {
        int newCapacity = _capacity == 0 ? 5 : 2 * _capacity;
        reserve(newCapacity);
    }

    size_t end = _size + 1;
    while (end > pos) {
        _str[end] = _str[end - 1];
        --end;
    }
    _str[pos] = ch;
    _size++;
    _str[_size] = '\0';
}

void insert(size_t pos, const char *str) {
    assert(pos <= _size);
    int size = strlen(str);
    reserve(_size + size);

    size_t end = _size + size;
    while (end > pos) {
        _str[end] = _str[end - size];
        --end;
    }
    strncpy(_str + pos, str, size);
    _size += size;
    _str[_size] = '\0';
}

void erase(size_t pos) {
    assert(pos <= _size);

    size_t begin = pos;
    while(begin < _size - 1) {
        _str[begin] = _str[begin + 1];
        ++begin;
    }
    _size--;
    _str[_size] = '\0';
}

查找元素

创建string类的静态成员函数npos,并设定值为-1。

static const size_t npos; // 声明
const size_t string::npos = -1;
size_t find(char ch, size_t pos = 0) const {
    for (size_t i = pos; i < _size; ++i) {
        if(_str[i] == ch) {
            return i;
        }
    }
    return npos;
}

size_t find(const char* str, size_t pos = 0) const {
    const char* ret = strstr(_str + pos, str);
    if(ret) {
        return ret - _str;
    } else {
        return npos;
    }
}

拷贝构造

比较容易想到的方法就是将char*一个字符一个字符拷贝。

string(const string &s)
        : _size(strlen(s._str)), _capacity(_size) {
    _str = new char[_capacity + 1];
    for (size_t i = 0; i < _size; i++) {
        _str[i] = s._str[i];
    }
}

也可以复用构造函数,构建一个临时的string对象,再交换this和临时对象,达到拷贝构造的效果。

string(const string &s)
        : _str(nullptr), _size(0), _capacity(_size) {
    string tmp(s._str);
    swap(tmp);
}

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

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

相关文章

PID控制算法学习笔记分享

目录 一、参数设定 二、PID计算公式 三、位置式PID代码实现 四、增量式PID代码实现 五、两种控制算法的优缺点 六、PID算法的改进 一、参数设定 比例系数&#xff08;kp&#xff09;&#xff1a;P项的作用是根据当前误差的大小来产生一个控制量。它直接与误差成正比&#…

Tensorflow入门实战 T05-运动鞋识别

目录 一、完整代码 二、训练过程 &#xff08;1&#xff09;打印2行10列的数据。 &#xff08;2&#xff09;查看数据集中的一张图片 &#xff08;3&#xff09;训练过程&#xff08;训练50个epoch&#xff09; &#xff08;4&#xff09;训练结果的精确度 三、遇到的问…

0618,0619 ,指针,指针与数组,字符串

目录 第八章&#xff08;指针&#xff09;&#xff0c;第九章&#xff08;指针和数组&#xff09;&#xff0c;第十章&#xff08;字符串&#xff09;思维导图 作业1 逆序打印 解答&#xff1a; 答案&#xff1a; 作业2 判断回文 解答&#xff1a; 答案&#xff1a; …

PCB设计隐藏的陷进

1、BGA芯片的开窗和过油设计。 加工工艺中&#xff0c;范式过孔都需要盖油设计&#xff0c;实心焊盘需要开窗设计&#xff0c;坚决不能盖油。 2、通孔设计的互联连通性 比如H3芯片的wifi设计&#xff0c;实际上是没有联通的&#xff0c;虽然四层板的中间层有焊盘&#xff0c;但…

【C++ | 移动构造函数】C++11的 移动构造函数 详解及例子代码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-12 2…

Mamaba3--RNN、状态方程、勒让德多项式

Mamaba3–RNN、状态方程、勒让德多项式 一、简单回顾 在Mamba1和Mamba2中分别介绍了RNN和状态方程。 下面从两个图和两个公式出发&#xff0c;对RNN和状态方程做简单的回顾&#xff1a; R N N : s t W s t − 1 U x t &#xff1b; O t V s t RNN: s_t Ws_{t-1}Ux_t&…

前字节员工自爆:我原腾讯一哥们,跳槽去小公司做小领导,就签了竞业,又从小公司离职去了对手公司,结果被发现了,小公司要他赔80万

“世界那么大&#xff0c;我想去看看”&#xff0c;这句曾经火遍网络的辞职宣言&#xff0c;说出了多少职场人心中的渴望。然而&#xff0c;当我们真的迈出跳槽那一步时&#xff0c;才发现&#xff0c;现实远比想象中残酷得多。 最近&#xff0c;一起前字节跳动员工爆料的事件…

22种常用设计模式示例代码

文章目录 创建型模式结构型模式行为模式 仓库地址https://github.com/Xiamu-ssr/DesignPatternsPractice 参考教程 refactoringguru设计模式-目录 创建型模式 软件包复杂度流行度工厂方法factorymethod❄️⭐️⭐️⭐️抽象工厂abstractfactory❄️❄️⭐️⭐️⭐️生成器bui…

自学网络安全 or Web安全,一般人我还是劝你算了吧

由于我之前写了不少网络安全技术相关的文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人私信问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f; 要学哪些东西&#xff1f; 有哪些方向&#xff1f; 怎么选&#xff1f;…

福昕PDF编辑器快速去除PDF水印方法

在福昕PDF编辑器软件中打开一个带有水印的PDF文件&#xff0c;点击如图下所示的页面管理->水印&#xff0c;点击全部移除 点击 是 水印消除&#xff08;注&#xff1a;部分类型的水印可以消除&#xff0c;但是有些类型的水印无法通过此方法消除&#xff09;

Day28:回溯法 491.递增子序列 46.全排列 47.全排列 II 332.重新安排行程 51. N皇后 37. 解数独 蓝桥杯 与或异或

491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情…

Flask新手入门(一)

前言 Flask是一个用Python编写的轻量级Web应用框架。它最初由Armin Ronacher作为Werkzeug的一个子项目在2010年开发出来。Werkzeug是一个综合工具包&#xff0c;提供了各种用于Web应用开发的工具和函数。自发布以来&#xff0c;Flask因其简洁和灵活性而迅速受到开发者的欢迎。…

昇思大模型学习·第一天

mindspore快速入门回顾 导入mindspore包 处理数据集 下载mnist数据集进行数据集预处理 MnistDataset()方法train_dataset.get_col_names() 打印列名信息使用create_tuple_iterator 或create_dict_iterator对数据集进行迭代访问 网络构建 mindspore.nn: 构建所有网络的基类用…

Docker(四)-Docker镜像

1.概念 镜像是一种轻量级的、可执行的独立软件包&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;我们把应用程序和配置依赖 打包好形成一个可交付的运行环境(包括代码&#xff0c;运行时需要的库&#xff0c;环境变量和配置文件等)&#xff0c;这个打包好的运行环境…

docker pull xxx拉取超时time out

文章目录 前言总结 前言 换了镜像源&#xff0c;改配置的都不行&#xff0c;弄了一个下午&#xff0c;最后运行一下最高指令就可以了 sudo docker_OPTS"--dns 8.8.8.8"总结 作者&#xff1a;加辣椒了吗&#xff1f; 简介&#xff1a;憨批大学生一枚&#xff0c;喜欢…

STM32自己从零开始实操06:无线电路原理图

一、WIFI 模块电路设计 1.1指路 延续使用 ESP-12S 芯片&#xff0c;封装 SMD 16x24mm。 实物图 原理图与PCB图 2.2电路图 电路较为简单&#xff0c;如下图&#xff1a; 2.2.1引脚说明 序号引脚名称描述1RST复位复位引脚&#xff0c;低电平有效3EN使能芯片使能端&#xff0c…

【深度学习】sdwebui A1111 加速方案对比,xformers vs Flash Attention 2

文章目录 资料支撑资料结论sdwebui A1111 速度对比测试sdxlxformers 用contorlnet sdxlsdpa&#xff08;--opt-sdp-no-mem-attention&#xff09; 用contorlnet sdxlsdpa(--opt-sdp-attention) 用contorlnet sdxl不用xformers或者sdpa ,用contorlnet sdxl不用xformers或者sdpa …

Windows安装配置jdk和maven(仅做记录)

他妈的远程连接不上公司电脑&#xff0c;只能在家重新配置一遍&#xff0c;在此记录一下后端环境全部配置 Windows安装配置JDK 1.8一、下载 JDK 1.8二、配置环境变量三、验证安装 Windows安装配置Maven 3.8.8一、下载安装 Maven并配置环境变量二、设置仓库镜像及本地仓库三、测…

BUU CODE REVIEW 11 代码审计之反序列化知识

打开靶场&#xff0c;得到的是一段代码。 通过分析上面代码可以构造下面代码&#xff0c;获取到序列化之后的obj。 <?php class BUU {public $correct "";public $input "";public function __destruct() {try {$this->correct base64_encode(u…

如何解决input输入时存在浏览器缓存问题?

浏览器有时会在你输入表单过后缓存你的输入&#xff0c;有时候能提供方便。 但是在某些新建或新页面情况下出现历史的输入信息&#xff0c;用户体验很差。 解决方案 设置 autocomplete关闭 &#xff1a;<input type"text" autocomplete"off">增加…