string的使用和模拟实现

news2024/11/15 3:45:24

💓博主个人主页:不是笨小孩👀
⏩专栏分类:数据结构与算法👀 C++👀 刷题专栏👀 C语言👀
🚚代码仓库:笨小孩的代码库👀
⏩社区:不是笨小孩👀
🌹欢迎大家三连关注,一起学习,一起进步!!💓

在这里插入图片描述

string

  • string的介绍
  • 结构
  • string的常用接口
    • 构造和析构
    • 遍历string
    • size和length
    • capacity
    • resize和reserve
    • 运算符重载
    • push_back和append
    • insert和erase
    • 查找
    • 截取字符串
    • 获取字符指针
    • 交换对象
    • 特殊读取

string的介绍

string是一个类,可以看成是一个用模板写的顺序表,它的底层结构和顺序表基本是一样的,一个字符指针和一个表示存储数据的个数的size,还有一个表示容量大小的capacity,我们知道C++需要兼容C语言,所以它的字符串后面也是需要有一个‘\0’,的但是size和capacity的大小是不包含这个‘\0’的,因此实际的容量要比capacity大一个。

  1. 字符串是表示字符序列的类。
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

  1. string是表示字符串的字符串类。
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
  4. 不能操作多字节或者变长字符的序列。

结构

string的结构和顺序表很相似,一个存放字符的指针,还有一个标记个数的size,还有一个标记容量的capacity。

class
{
  private:
      char* _str;
      size_t _size;
      size_t _capacity;
}

string的常用接口

构造和析构

构造函数我们可以用个缺省参数,直接搞定无参和有参的情况,然后使用初始化列表把size和capacity给初始化一下,然后开个空间把参数拷贝过去就可以了。
析构函数的话很简单,直接释放空间即可,把其他值置一下0即可。

//构造
string(const char* s = "")
    : _size(strlen(s))
    , _capacity(_size)
{
    _str = new char[_capacity + 1];
    strcpy(_str, s);
}
//析构
~string()
{
    delete[] _str;
    _str = nullptr;
    _size = _capacity = 0;
}

遍历string

  1. 我们可以使用for循环来遍历
    在这里插入图片描述
    因为库里的string支持了[]运算符重载,我们可以就像遍历数组那样来遍历string。
int main()
{
    string s("123456");
    for (int i = 0; i < 6; i++)
    {
        cout << s[i] << " ";
    }
    cout << endl;

    return 0;
}

因为它是传引用返回的,所以我们可读可写。它重载了一个const版本,所以当你传const对象时,就只能读不能写。

int main()
{
	string s("123456");
	for (int i = 0; i < 6; i++)
	{
		s[i]++;
	}
	cout << s << endl;
	return 0;
}
  1. 使用迭代器遍历

迭代器我们可以理解为一个指针,然后需要一个范围,库里有几个函数,可以帮我们确定这个范围。

在这里插入图片描述
如果我们要正着遍历可以使用begin和end。

在这里插入图片描述
在这里插入图片描述
它的返回值是一个iterator的迭代器类型,然后这个类型在string类里面的,所以我们需要确定类域。

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

    return 0;
}

如果要倒着遍历,就需要使用了,rbegin和rend。
在这里插入图片描述
在这里插入图片描述
只不过他们的返回类型是reverse_iterator。

int main()
{
    string s("123456");
    string::reverse_iterator it = s.rbegin();
    while (it != s.rend())
    {

        cout << *it << " ";
        it++;
    }
    cout << endl;

    return 0;
}

这两对都是可读可写的,还剩两对前面带c的,是只能读,不能写。具体可戳标题详解。

  1. 使用范围for
int main()
{
	string s("123456");
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

size和length

size和length的功能是一样的,都是返回字符串的长度,和C语言的strlen很相似。

在这里插入图片描述

在这里插入图片描述

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

capacity

capacity是返回已经开辟的容量的大小。

在这里插入图片描述

int main()
{
	string s("123456");
	cout << s.capacity() << endl;
	return 0;
}

模拟实现

const size_t size() const
{
    return _size;
}
const size_t capacity() const
{
    return _capacity;
}

resize和reserve

  1. resize

resize可以修改size的大小。

在这里插入图片描述
库里重载两个版本,resize分3个版本,它可能比capacity大,也可能在size和capacity中间,也有可能比size小。如果他比capacity大的话,会扩容,并且如果我们给字符参数,就会在我们原本的字符串后面全补我们给的那个字符,直到size变成我们设置的那个,我们没给字符的话,会补‘\0’,如果在size和capacity中间,不会扩容,但是还是会补充,如果比我们的size还小的话,就充当删除的功能了。

int main()
{
	string s("123456");
	s.resize(20);
	s.resize(20,'x');
	s.resize(10);
	s.resize(4);
	return 0;
}
  1. reserve

reserve可以设置capacity的大小,如果我们知道字符串多大的话,我们可以提前开好空间,因为让系统自己自动扩容的话,可能会频繁扩容,消耗太大了,所以我们在知道需要存储的字符串多大时可以提起开好空间。

在这里插入图片描述

int main()
{
	string s("123456");
	cout << s.capacity() << endl;
	s.reserve(100);
	cout << s.capacity() << endl;

	return 0;
}

模拟实现

void reserve(size_t n)
{
     if (n > _capacity)
     {
         char* tmp = new char[n + 1];
         strcpy(tmp, _str);
         delete[] _str;
         _str = tmp;

         _capacity = n;
     }
 }
void resize(size_t n, char c = '\0')
{
    if (n <= _size)
    {
        _str[n] = '\0';
        _size = n;
    }
    else
    {
        reserve(n);
        size_t begin = _size;
        _size = n;

        while (begin < n)
        {
            _str[begin] = c;
            begin++;
        }

        _size = n;
        _str[begin] = '\0';
        
    }
}

运算符重载

  1. 赋值运算符

在这里插入图片描述

也就是说我们可以直接用string对象或者字符串来给新的对象赋值。

int main()
{
	string s;
	s = "123456";
	string s1("xxx");
	s = s1;
	return 0;
}
  1. +和+=

在这里插入图片描述

在这里插入图片描述

从这两张图我们就可以看出来,+=是成员函数,而+是全局函数。我们需要尾插是可以用+=也可以用+,但是+的代价是很大的,我们尽量使用+=。

int main()
{
	string  s("123");
	s += "456";
	s += '7';
	s = s + '8';
	return 0;
}

模拟实现

string& operator+=(const char s)
{
    push_back(s);

    return *this;
}

string& operator+=(const char* s)
{
    append(s);

    return *this;
}
  1. 流插入和流提取
    在这里插入图片描述
    在这里插入图片描述

也就是说,我们可以直接使用cout和cin来对sting对象来进行输入和输出了。

模拟实现

  1. 流提取
    我们可以一个字符一个字符的提取,因为_str为私有的,我们无法访问,我们也可以使用友元,但是我们这里一个字符一个字符的访问。
ostream& operator<<(ostream& out, const string& s)
{
    int end = s.size();
    for (int i = 0; i < end; i++)
    {
        out << s[i];
    }
    return out;
}
  1. 流插入

流插入也一样,我们一个字符一个字符的读,当读到‘\n’或者空格就停止,但是cin和scanf都无法读空格和换行,这是就需要一个函数,cin.get(),它的功能就和C语言的getcahr很相似。

在这里插入图片描述

istream& operator>>(istream& in, string& s)
{
    s.clear();
    char ch = in.get();

    while (ch != ' ' && ch != '\n')
    {
        s += ch;
        ch = in.get();
    }

    return in;
}
int main()
{
	string s;
	cin >> s;
	cout << s << endl;
	return 0;
}
  1. 比较运算符
    在这里插入图片描述
    我们可以直接使用比较运算符来对sting对象和字符串等进行比较了。

模拟实现

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

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

string& operator+=(const char s)
{
    push_back(s);

    return *this;
}

string& operator+=(const char* s)
{
    append(s);

    return *this;
}

bool operator<(const string& s)
{
    return strcmp(_str, s._str) < 0;
}

bool operator==(const string& s)
{
    return strcmp(_str, s._str) == 0;
}

bool operator<=(const string& s)
{
    return *this<s||*this==s;
}

bool operator>(const string& s)
{
    return !(*this<=s);
}

bool operator>=(const string& s)
{
    return !(*this<s);
}

bool operator!=(const string& s)
{
    return !(*this==s);
}

push_back和append

  1. push_back
    在这里插入图片描述

尾插一个字符

  1. append
    在这里插入图片描述

尾插一段字符串,库里面给的接口很多,我们都不是很常用,我们都喜欢用+=。

模拟实现

void push_back(const char ch)
{
    if (_size == _capacity)
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }

    _str[_size++] = ch;
    _str[_size] = '\0';
    
}
void append(const char* s)
{
    size_t len = strlen(s);
    if (_size + len > _capacity)
    {
        reserve(_size+len);
    }

    strcpy(_str + _size, s);

    _size += len;
}

insert和erase

  1. insert
    在这里插入图片描述

我们一般用的都是在某个位置插入一个字符或者字符串。

模拟实现

string& insert(size_t pos, char ch)
{
    assert(pos <= _size);

    if (_size == _capacity)
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }

    int end = _size;
    //必须强转,不然会类型提升,当pos=0时发生死循环
    while (end >= (int)pos)
    {
        _str[end + 1] = _str[end];
        end--;
    }
    _str[pos] = ch;
    _size++;
    return *this;
}

string& insert(size_t pos, const char* str)
{
    size_t len = strlen(str);
    if (_size + len > _capacity)
    {
        reserve(_size + len);
    }

    int end = _size;
    while (end >= (int)pos)
    {
        _str[end + len] = _str[end];
        end--;
    }

    strncpy(_str + pos, str, len);

    _size += len;

    return *this;
}
  1. erase
    在这里插入图片描述

删除某个位置的len个字符

模拟实现

earse我们需要提供一个npos的半缺省参数,如果不给的话就表示将pos以后得数据都删了。
nops是一个所有类都有的,所以我们可以定义为静态成员变量。

string& erase(size_t pos, size_t len = npos)
{
  assert(pos < _size);
   if (pos + len > _size || len == npos)
   {
       _str[pos] = '\0';
       _size = pos;
       return *this;
   }
   else
   {
       size_t begin = pos + len;
       while (begin <= _size)
       {
           _str[begin - len] = _str[begin];
           begin++;
       }
       _size -= len;
       return *this;
   }
}
public:
        const static int npos = -1;

查找

  1. find
    在这里插入图片描述
    我们可以用这个函数来查找字符串或者字符。
int main()
{
	string s("123456");
	int i = s.find("23");
	int j = s.find('5',2);
	int x = s.find("123", 2, 3);
	return 0;
}
  1. rfind
    在这里插入图片描述
    rfind也是查找只不过是从最后开始找。和find相似。

模拟实现

size_t find(char c, size_t pos = 0) const
 {
     assert(pos < _size);
     for (size_t i = pos; i < _size; i++)
     {
         if (_str[i] == c)
         {
             return i;
         }
     }
     return npos;
 }

 size_t find(const char* s, size_t pos = 0) const
 {
     size_t len = strlen(s);
     assert(pos + len < _size);

     char* str = strstr(_str + pos, s);

     if (str != nullptr)
     {
         return str - _str;
     }

     return npos;
 }

截取字符串

substr
在这里插入图片描述

从pos位置开始提取len个字符,不给len直接取到最后。

模拟实现

string substr(size_t pos = 0, size_t len = npos) const
{
    string s;
    if (len == npos || pos + len > _size)
    {
        size_t i = 0;
        for (i = pos; i <= _size; i++)
        {
            s += _str[i];
        }
        return s;
    }
    else
    {
        size_t i = 0;
        for (i = pos; i < pos+len; i++)
        {
            s += _str[i];
        }
        s += '\0';
        return s;
    }
}

获取字符指针

我们知道string是一个类,那我们想要得到底层的那个字符指针怎么获得呢?

我们可以使用c_str这个函数
在这里插入图片描述


int main()
{
	string s("123456");
	const char* str = s.c_str();
	return 0;
}

模拟实现

const char* c_str() const
{
    return _str;
}

交换对象

swap
在这里插入图片描述

对于string有一个专门的swap,可以用来交换两个string对象。

int main()
{
	string s("123456");
	string s1("12");
	s.swap(s1);
	cout << s << endl << s1 << endl;
	return 0;
}

模拟实现

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

特殊读取

我们知道cin和scanf一样,遇到空格或者换行都会提前结束,那我们需要读取的字符串包含空格或者换行怎么办呢,这时候就需要getline了。

在这里插入图片描述

int main()
{
	string s;
	getline(cin, s);
	cout << s;
	return 0;
}

我们也可以自己指定读取结束的字符。

今天的分享就到这里,感谢大家的关注和支持。

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

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

相关文章

Pytest系列-使用自定义标记mark(6)

简介 pytest 可以支持自定义标记&#xff0c;自定义标记可以把一个 web 项目划分为多个模块&#xff0c;然后指定模块名称执行 Pytest 里面自定义标记 用法&#xff1a;将pytest.mark.标记名称 放到测试函数或者类上面 使用&#xff1a; 执行时加上 -m 标记名 进行用例筛选…

[交互]交互的实战问题1

[交互]交互的实战问题1 状态码 431 Request Header Fields Too LargeReferrer Policy: no-referrer-when-downgrade路径参数高并发问题使用场景使用的方法异常情况 状态码 431 Request Header Fields Too Large 最近做项目&#xff0c;遇到一个问题&#xff0c;后台导出表格时…

牛客: BM4 合并两个排序的链表

牛客: BM4 合并两个排序的链表 文章目录 牛客: BM4 合并两个排序的链表题目描述题解思路题解代码 题目描述 题解思路 以链表一为主链表,遍历两条链表 若当前链表二的节点val小于当前链表一的下一个节点val,则将链表链表二的该节点连到链表一的节点的下一个,链表一的当前节点往…

sql存储引擎

-- 查询建表语句 --可以查看引擎 show create table account; -- 可以看到默认引擎 InnoDB ENGINEInnoDB -- 查看当前数据库支持得存储引擎 show engines ; # InnoDB 默认 存储引擎 # MyISAM sql早期默认 存储引擎 # MEMORY 存储在内存中 用来做临时表和缓存 存储引擎 …

Adobe Acrobat Reader 中的漏洞

另一个流行漏洞 Adobe Acrobat 和 Acrobat Reader - 流行的便携式文档格式 (PDF) 工具 - 存在风险。该漏洞 CVE-2023-26369影响 Windows 和 macOS 安装。 攻击者创建的恶意 PDF 文档打开后&#xff0c;会利用与在缓冲区外写入有关的 CVE-2023-26369漏洞。因此&#xff0c;攻击…

数据中心液冷服务器详情说明

目录 前言 何为液冷服务器&#xff1f; 为什么需要液冷&#xff1f; 1.数据中心降低PUE的需求 2.政策导向 3.芯片热功率已经达到风冷散热极限 4.液冷比热远大于空气 液冷VS风冷&#xff0c;区别在哪&#xff1f; 1.液冷服务器跟风冷服务器的区别 2.液冷数据中心跟风冷…

linux安装常见的中间件和数据库

文章目录 一、数据库二、redis三、tomcat四、nginx五、mq六、es七、nacos八、neo4j&#xff08;图数据库&#xff09;九、fastdfs其他 一、数据库 linux环境上使用压缩包安装mysql【数据库】Mysql 创建用户与授权 二、redis redis是没有账号的&#xff0c;只能设置密码Linux…

对IP协议概念以及IP地址的概念进行简单整理

网络层重要协议 参考模型和协议栈IP协议IPv4数据报IP数据报格式IPv4地址特殊IP地址私有IP地址和公有IP地址子网划分 参考模型和协议栈 IP协议 IP协议定义了网络层数据传送的基本单元&#xff0c;也制定了一系列关于网络层的规则。 IPv4数据报 网络层的协议数据单元PDU 叫做分…

GeoSOS-FLUS未来土地利用变化情景模拟模型

软件简介 适用场景 GeoSOS-FLUS软件能较好的应用于土地利用变化模拟与未来土地利用情景 的预测和分析中&#xff0c;是进行地理空间模拟、参与空间优化、辅助决策制定的有效工 具。FLUS 模型可直接用于&#xff1a; 城市发展模拟及城市增长边界划定&#xff1b;城市内 部高分…

分布式事务解决方案之TCC

分布式事务解决方案之TCC 什么是TCC事务 TCC是Try、Confirm、Cancel三个词语的缩写&#xff0c;TCC要求每个分支事务实现三个操作&#xff1a;预处理Try、确认 Confirm、撤销Cancel。Try操作做业务检查及资源预留&#xff0c;Confirm做业务确认操作&#xff0c;Cancel实现一个…

Golang代码漏洞扫描工具介绍——govulncheck

Golang Golang作为一款近年来最火热的服务端语言之一&#xff0c;深受广大程序员的喜爱&#xff0c;笔者最近也在用&#xff0c;特别是高并发的场景下&#xff0c;golang易用性的优势十分明显&#xff0c;但笔者这次想要介绍的并不是golang本身&#xff0c;而且golang代码的漏洞…

微信小程序+echart实现点亮旅游地图

背景 最近看抖音有个很火的特效就是点亮地图&#xff0c;去过哪些地方&#xff0c;于是乎自己也想做一个&#xff0c;结合自己之前做的以家庭为单位的小程序&#xff0c;可以考虑做一个家庭一起点亮地图的功能。 效果图 过程 1&#xff0c;首先就是得去下微信小程序适配的ec…

react 实现拖动元素

demo使用create-react-app脚手架创建 删除一些文件&#xff0c;创建一些文件后 结构目录如下截图com/index import Movable from ./move import { useMove } from ./move.hook import * as Operations from ./move.opMovable.useMove useMove Movable.Operations Operationse…

ABB 1TGE120010R... Rev控制模块

ABB 1TGE120010R... Rev 控制器模块是一种高性能控制器&#xff0c;可用于工业自动化和过程控制应用。它具有以下主要特点&#xff1a; 多功能性&#xff1a;该控制器模块可用于多种应用&#xff0c;包括机器控制、过程控制和自动化系统等。 高性能&#xff1a;该控制器模块具…

竞赛选题 基于机器视觉的火车票识别系统

文章目录 0 前言1 课题意义课题难点&#xff1a; 2 实现方法2.1 图像预处理2.2 字符分割2.3 字符识别部分实现代码 3 实现效果最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于机器视觉的火车票识别系统 该项目较为新颖&#xff0c;适合作为竞赛…

408强化(番外)文件管理

有点看不下去书&#xff0c;408&#xff0c;哎好久没看了&#xff0c;死磕数学时完全不想看其他科目&#xff0c;数学分数也尚未质变。 突然想到一个好点子&#xff0c;只看大纲尝试回忆一下这章的内容。 文件就是为了方便用户使用&#xff0c;按名访问而提出的&#xff0c;从…

Python进阶教学——多线程高级应用

目录 一、线程间的通讯机制 二、线程中的消息隔离机制 三、线程同步信号量 四、线程池和进程池 一、线程间的通讯机制 1、Queue消息队列 消息队列是在消息的传输过程中保存消息的容器&#xff0c;主要用于不同线程间任意类型数据的共享。消息队列最经典的用法就是消费者和…

【Linux】项目自动化构建工具 make/Makefile

1、背景原理 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile 定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件需要后编译&#xff0c;哪些文件需要重新编译&#xff0c;甚至于进行更复杂的功…

Day 01 web前端基础知识

首先我们要了解什么事前端&#xff1f; 先简单用文字介绍一下&#xff1a; 一、入门知识 Web前端是指网站或应用程序的用户界面部分。它包括HTML、CSS、JavaScript等语言和技术&#xff0c;用于创建用户可浏览和交互的网页。Web前端的特点在于其交互性和动态性&#xff0c;可…

富芮坤蓝牙FR801xH GPIO

通过规格书&#xff0c;可查看到芯片共有32个引脚&#xff0c;如图&#xff1a; 除如电源、晶振等固定用途的引脚外&#xff0c;开发板已引出其余引脚。 通常情况下&#xff0c;一个IO口除了可作普通输入输出口外&#xff0c;还有可能作其它用途&#xff0c;如作I2C接口的数据…