C++ string

news2024/11/25 2:01:33

目录

    • string类介绍
    • 访问:
      • [ ] 遍历
      • 迭代器遍历
      • 范围for遍历
    • 容量相关:
    • 修改相关:
    • 编码表的了解
    • 写时拷贝的了解
    • string的模拟

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

string类介绍

string属于标准库,因为它的产生比stl早一些
C++为了避免跟c语言库冲突,头文件不带.h

string可以管理字符数组

求长度不能用strlen,strlen不能对自定义对象使用,而且自定义对象里面的内置类型的私有成员也不能随便访问,不同编译器下私有成员名字可能不一样。
在这里插入图片描述

平时用string没有用模板,实际上它是模板,只是库typedef过,因为大多数情况用的都是char,所以就实例化了

在这里插入图片描述
1、4是常用的构造,2是拷贝构造,3是用str对象的一部分去构造,5是用str的前n个初始化,6是用n个字符c去初始化,7可以用迭代区间去初始化
在这里插入图片描述

npos是string里的静态成员变量,类型是size_t, -1类型提升,全1的无符号是整形的最大值
在这里插入图片描述

    string s1;
    string s2("hello");
    
    cin >> s1;
    cout << s1 << endl;
    cout << s2 << endl;
    
    //char str[1600];对比c语言,c++按需申请使用会更方便
    //scanf()
    
    //想拼接两个string的话
    string ret1 = s1 + s1;
    cout << ret1 << endl;
    //和字符串相加也可以
    string ret2 = s1 + "你好";
    cout << ret2 << endl;
    //相比于c语言中的strcat可读性会更好,而且strcat也不能扩容
    //strcat得找'\0',string中有size(),可以直接找到结尾
string s1("hello world");
string s2 = "hello world";

两种带参构造都可以用

用常量字符串初始化
第一种是构造
第二种是构造+拷贝构造,单参数的构造函数支持隐式类型转换

访问:

[ ] 遍历

在这里插入图片描述

// 遍历string
    for (size_t i = 0; i < s1.size(); i++)
    {
        // 读
        cout << s1[i] << " ";
    }
    cout << endl;

    for (size_t i = 0; i < s1.size(); i++)
    {
        // 写
        s1[i]++;
    }
    cout << s1 << endl;

at是一个函数跟[ ] 重载 不同的是at失败后(比如越界)会抛异常,[]是断言,会终止程序。
在这里插入图片描述

s2[0]++;
s2.at(0)++;

[]用起来会更直观

迭代器遍历

迭代器是遍历数据结构的一种方式,目前可以想象成指针,因为使用起来很像
每个数据结构的迭代器都是在类里面定义(或typedef)的,属于这个类域

// 迭代器
    string::iterator it = s1.begin();
    //while (it < s1.end()) 
    //这里可以但是不建议,因为如果是别的数据结构就不支持了,比如list
    
    while (it != s1.end())  // 推荐玩法,通用
    {
        // 读
        cout << *it << " ";
        ++it;
    }
    cout << endl;


    it = s1.begin();
    while (it != s1.end())
    {
        // 写
        *it = 'a';
        ++it;
    }
    cout << endl;
    cout << s1 << endl;

在这里插入图片描述
[ ]用起来方便但不是每个数据结构都能用,它针对底层是连续的物理空间的数据结构,迭代器才是通用的方式
迭代器屏蔽了底层细节,很好体现了c++的封装
在这里插入图片描述
在这里插入图片描述
cbegin这些是想把const iterator区分出来,但其实还是直接用begin就好
在这里插入图片描述

在这里插入图片描述

普通的iterator支持读和写,const iterator只能读
const对象要调用const版本的begin,const版本begin返回const的迭代器
所以迭代器要和s.begin()返回一致

void func(const string& s)
{
    //string::const_iterator it = s.begin();
    auto it = s.begin();
    while (it != s.end())
    {
        // 不支持写
        // *it = 'a';

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

    //string::const_reverse_iterator rit = s.rbegin();
    auto rit = s.rbegin();
    while (rit != s.rend())
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
}

void test_string4()
{
    string s1("hello worldxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyy");
    func(s1);
}

范围for遍历

    // 原理:编译器编译器替换成迭代器
    // 读
    for (auto ch : s1)
    {
        cout << ch << " ";
    }
    cout << endl;

    // 写
    for (auto& ch : s1)//ch相当于*it的拷贝,所以加&才能修改
    {
        ch++;
    }
    cout << endl;

    cout << s1 << endl;

容量相关:

size和length
在这里插入图片描述
在这里插入图片描述
string的产生比stl早,所以当时用的是length
size 具有通用性
‘\0’标识结束,size不算’\0’
动态增长的数组还需要’\0’是因为要兼容c语言
让string能和c的接口结合起来

string filename;
    cin >> filename;
    FILE* fout = fopen(filename.c_str(), "r");

在这里插入图片描述
clear清除数据,stl没有严格规定是否释放空间,一般是不会释放的,因为清掉之后可能还要继续插入数据
max_size本意是想告诉我们string最长有多长,不同平台下实现不同,它是写死的,在我们内存不够时告诉我们也开不了。所以基本用不上。

扩容是怎么扩的?

void test_string6()
{
    string s;

    size_t old = s.capacity();
    cout << "初始" << s.capacity() << endl;

    for (size_t i = 0; i < 100; i++)
    {
        s.push_back('x');

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

在这里插入图片描述
在vs上我们会发现string s,它没有存数据,容量居然是15,也就是16的空间,这是因为开了一个16字节的数组
小于16,字符串存到buff数组里面
大于等于16,存在_str指向的空间,buff里面的空间就不要了

这是为了避免系统频繁开小块内存而引发内存碎片
平时string也比较小

    _buff[16]
	_str
	_size
	_capacity

reserve和resize
在这里插入图片描述
g++下要100就会给100,vs2019下要100会给111
可以提前开空间,减少扩容,提高效率

stl没有规定reserve能不能减少空间,一般实现是不会减的(比如g++和vs)

在这里插入图片描述

不给c默认是给’\0’
在这里插入图片描述
让size变到n

修改相关:

插入单个字符
在这里插入图片描述

插入字符串
在这里插入图片描述

//用的多的也就3和1
   string ss("world");

   string s;
   s.push_back('#');
   s.append("hello");
   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;

+的效率会较低(拷贝构造再传值返回),尽量用+=

在这里插入图片描述
assign,赋值,会把原来的数据覆盖

std::string str("xxxxxxx");
std::string base = "The quick brown fox jumps over a lazy dog.";

str.assign(base);
std::cout << str << '\n';

在这里插入图片描述
insert/erase/repalce能不用就尽量不用,因为他们都涉及挪动数据,效率不高
接口设计复杂繁多,需要时查一下文档即可

在这里插入图片描述
string中swap的有效使用场景

空格替换为20%
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%";
        }
    }

在这里插入图片描述
模板的swap代价太高,string全局函数还提供了一个swap,所以就不会调用模板的swap在这里插入图片描述

find系列

find从字符串pos位置开始往后找字符c
rfind从字符串pos位置开始往前找字符c
substr找子串:从pos位置开始,截取n个字符,然后返回
在这里插入图片描述

void test_string11()
{
    //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)
        sub1 = s3.substr(0, i1);
    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;
}

编码表的了解

string为什么设计成模板?

内存中不能存a、b、c这种概念,内存中只有01,只能组合成值,值和符号之间要有联系就要建立一一映射的关系,这就是编码表

每个国家都需要一套编码表
我们的文字很多,一个字节一个汉字是不够表示的(8个bit有2^8个值)

在这里插入图片描述
各个国家文字数量的不同
主要有三类方案 UTF-8、UTF-16、UTF-32
这是国际上的,为了更好表示中文,我们也有自己的一份编码表比如GBK。
在这里插入图片描述
兼容ascll,常用的汉字用两个字节(110 10)编,相对生僻的用三个字节(110 10 10)编,特别生僻的用四个字节编

在这里插入图片描述
在这里插入图片描述

假设一段文字是用UTF-8写的,然后改成另外一个编码,查的时候对不上,就会出现乱码(存储格式和解释方式对应不上)
我们有时候会出现烫烫烫烫烫是因为数组没有初始化的时候是随机值,这个随机值是1开头被解释成汉字,查编码表正好是烫,取决于编译器用的是什么编码。

写时拷贝的了解

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

string的模拟

vs下:在类里定义静态成员变量时发现在类里可以定义的特例

const static size_t npos = -1;//针对const 静态的 整形类型开了一个特例
const static double npos = 1.1; //double就不行了

#include<iostream>
#include<assert.h>
using namespace std;

namespace st
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		
		//string(const string& str)
		//{
		//	_str = new char[str._capacity + 1];
		//	strcpy(_str, str._str);
		//	_size = str._size;
		//	_capacity = str._capacity;
		//}
		//简化后
		string(const string& str)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(str._str);//进构造

			swap(tmp);
			//this里的数据可能是随机值,如果把随机值跟tmp交换,
			//然后tmp出作用域之后调用析构,会把随机值空间释放,所以需要初始化_str
		}
		
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		
		/*string& operator=(const string& s)
		{
			char*tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);

			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
			return *this;
		}*/
		/*string& operator=(const string& s)
		{
			string tmp(s);
			swap(tmp);
			return *this;
		}*/
		//简化后
		//写好拷贝构造就相当于写好了赋值
		string& operator=(string tmp)
		{
			swap(tmp);
			return *this;
		}

		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		size_t size() const
		{
			return _size;
		}
		size_t capacity() const
		{
			return _capacity;
		}
		const char* c_str()const
		{
			return _str;
		}

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

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

		void push_back(char c)
		{
			assert(_size <= _capacity);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}
		void append(const char* s)
		{
			int len = strlen(s);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
			strcpy(_str + _size, s);
			_size += len;
		}
		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
		string& operator+=(const char* s)
		{
			append(s);
			return *this;
		}

		bool empty()const
		{
			return _size == _capacity;
		}

		bool operator>(const string& str)const
		{
			for (int i = 0; i < str.size(); i++)
			{
				if (i == this->size())
					return true;

				if (this->_str[i] > str._str[i])
					return true;
				else if (this->_str[i] < str._str[i])
					return false;
			}
			return false;
		}
		bool operator==(const string& str)const
		{
			for (int i = 0; i < str.size(); i++)
			{
				if (i == this->size() || this->_str[i] != str._str[i])
					return false;
			}
			return true;
		}
		bool operator<(const string& str)const
		{
			return !(*this >= str);
		}
		bool operator>=(const string& str)const
		{
			return *this > str || *this == str;
		}
		bool operator<=(const string& str)const
		{
			return *this < str || *this == str;
		}
		bool operator!=(const string& str)const
		{
			return !(*this == str);
		}
		size_t find(char c, size_t pos = 0)const
		{
			for (int i = pos; i < (*this).size(); i++)
			{
				if ((*this)._str[i] == c)
				{
					return i;
				}
			}
			return -1;
		}
		size_t find(const char* s, size_t pos = 0)const
		{
			char* str = strstr(_str + pos, s);
			return str == nullptr ? -1 : str - _str;
		}
		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = len+pos;
			if (len == npos || pos + len >= _size)
			{
				len = _size - pos;
				end = _size;
			}
			s.reserve(len);
			while (pos < end)
			{
				s += _str[pos];
				pos++;
			}
			return s;
		}
		string& insert(size_t pos, char c)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			for (int i = size(); i > pos; i--)
			{
				_str[i] = _str[i - 1];
			}
			_str[pos] = c;
			_size++;
			_str[_size] = '\0';
			return *this;
		}
		string& insert(size_t pos, const char* str)
		{
			int len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
			_size += len;
			for (int i = size(); i >= pos+len; i--)
			{
				_str[i-1] = _str[i - len - 1];
			}
			for (int i = 0; i < len; i++)
			{
				_str[i + pos] = str[i];
			}
			_str[_size] = '\0';
			return *this;
		}
		string& erase(size_t pos, size_t len = string::npos)
		{
			if (len == npos || pos+len >= size())
			{
				_str[pos] = '\0';
				_size = pos;
				return *this;
			}
			for (size_t i = pos + len; i <=size(); i++)
			{
				_str[i - len] = _str[i];
			}
			_size -= len;
			return *this;
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		void swap(string& s)
		{
			char* str = s._str;
			size_t size = s._size;
			size_t capacity = s._capacity;

			s._str = _str;
			s._size = _size;
			s._capacity = _capacity;

			_str = str;
			_size = size;
			_capacity = capacity;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		const static size_t npos;
	};
	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char buff[129];
		int i = 0;
		char ch;
		ch=in.get();

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i != 128)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}
}

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

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

相关文章

【Linux】高级IO --- 多路转接,select,poll,epoll

所有通过捷径所获取的快乐&#xff0c;无论是金钱、性还是名望&#xff0c;最终都会给自己带来痛苦 文章目录 一、五种IO模型1.什么是高效的IO&#xff1f;&#xff08;降低等待的时间比重&#xff09;2.有哪些IO模型&#xff1f;哪些模型是高效的&#xff1f;3.五种IO模型的特…

【韩顺平 零基础30天学会Java】面向对象编程(基础)(5days)

面向对象编程 Java设计者引入类与对象&#xff08;OOP&#xff09;&#xff0c;根本原因就是现有的技术&#xff0c;不能完美的解决新的需求。 对象主要是由属性和行为构成的。 类就是数据类型&#xff08;比如Cat&#xff09;&#xff0c;对象就是具体的实例。 //定义一个猫…

QT转型Visual Studio(qmake项目到cmake项目的移植)

前言 由于工作需要&#xff0c;同时也为了方便以后的集成升级&#xff0c;希望将之前用Qt creator qmake开发的项目移植到cmake项目中&#xff0c;并使用Visual Studio 进行后续开发&#xff0c;本文主要用以记录该过程中的常规步骤和遇到的特殊情况。 qmake项目 在一开始&a…

MATLAB 的 figure 用法总结

文章目录 Syntax&#xff1a;DescriptionExamples1.figure2.figure(Name,Value)Position 属性: 设置 Figure 的位置和大小Name 属性: 设置 Figure 的名称NumberTitle 属性: 取消 Figure 名称里默认的数字units 属性color 属性 3.f figure(___)4.Working with Multiple Figures…

JAVA反序列化漏洞复现

Weblogic&#xff08;CVE-2017-10271&#xff09; 拉取容器 访问 http://192.168.142.151:7001/console/login/LoginForm.jsp ​ 启动nacs 进行漏洞扫描 下载weblogicScanner工具 git clone https://github.com/0xn0ne/weblogicScanner.git 开始扫描 访问http://192.168.1…

数据结构队列

数据结构队列 队列的概念及结构队列的是实现数据结构函数接口初始化销毁入队列&#xff08;尾插&#xff09;出队列&#xff08;头删&#xff09;求队列的长度判断队列是否为空取队头的数据取队尾的数据 队列的概念及结构 队列之允许在一段及进行数据插入操作&#xff0c;在另…

LeetCode 356. Line Reflection【数学,哈希表】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

IO day1

1->x.mind 2-> 3->计算一个文件的大小&#xff0c;计算一个文件有几行。封装成函数

MyBatisPlus查询条件设置、映射匹配兼容性、id生成策略、多数据操作

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 MyBatisPlus 一、 查询条件设置二、 映射匹配兼容…

数据结构day7栈-链式栈原理及实现

全部代码&#xff1a; main.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include "linkstack.h"int main(int argc, char *argv[]) {linkstack s;s stack_create();if(s NULL){return -1;}stack_push(s, 10);stack_push(s…

[图文并茂]C++线性表及其逻辑结构

1.1线性表的定义 线性表是具有相同特性的数据元素的一个有限序列 对应的逻辑结构图形&#xff1a; 从线性表的定义中可以看出它的特性&#xff1a; &#xff08;1&#xff09;有穷性&#xff1a;一个线性表中的元素个数是有限的 &#xff08;2&#xff09;一致性&#xff1a…

注册登录首选,趣味滑块验证码

前言 注册登录账户时&#xff0c;保障账户安全是首要任务&#xff01;使用趣味滑块验证码&#xff0c;既能有效防御恶意攻击&#xff0c;又能为验证过程增添一丝乐趣。让注册和登录变得更加有趣又安全&#xff01; HTML代码 <script src"https://cdn6.kgcaptcha.co…

Ubutnu python2与python3切换

python -V #查看默认版本 Python 2.7.17 python3 -V #查看电脑3的版本 Python 3.6.9 sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.6 2 #设置两个版本的…

【Linux】虚拟地址空间理解

虚拟地址空间 虚拟地址是操作系统管理内存的一种方式。**方便不同进程使用的虚拟地址彼此隔离。方便物理内存中不相邻的内存在虚拟地址上视为连续的来使用。虚拟地址和物理地址的映射是通过MMU页表进行的。虚拟内存对实际内存有保护作用。

LeetCode(力扣)17. 电话号码的字母组合Python

LeetCode17. 电话号码的字母组合 题目链接代码 题目链接 https://leetcode.cn/problems/letter-combinations-of-a-phone-number/ 代码 class Solution:def __init__(self):self.letterMap ["", # 0"", # 1"abc", # 2"def&qu…

备战9月9日C/C++青少年等级考试(1~8级)

由中国电子学会举办的《全国青少年软件编程等级考试》将于9月9日&#xff08;周六&#xff09;举行&#xff0c;你准备的怎么样了&#xff1f;我在这里列举了1~8级的历届真题及解析&#xff0c;希望能助你考试通过&#xff01;&#xff01;&#xff01; C/C编程一级 一级标准 …

Go调用jenkins api执行流水线构建与停止

用到的库&#xff1a; "github.com/bndr/gojenkins" 代码如下&#xff0c;一次到位&#xff1a; import ("context""fmt""time""github.com/bndr/gojenkins" )// 构建指定任务 func buildJob(ctx context.Context, jenkins…

CUDA 问题 ,一直头大。。。。

1.卸载cuda ubuntu系统安装/卸载cuda和cudnn_怎么删除cudnn_Zhijun.liStudio的博客-CSDN博客ubuntu系统安装/卸载cuda和cudnn_怎么删除cudnnhttps://blog.csdn.net/weixin_45921929/article/details/128849198?ops_request_misc%257B%2522request%255Fid%2522%253A%252216939…

机器学习算法系列————决策树(二)

1.什么是决策树 用于解决分类问题的一种算法。 左边是属性&#xff0c;右边是标签。 属性选择时用什么度量&#xff0c;分别是信息熵和基尼系数。 这里能够做出来特征的区分。 下图为基尼系数为例进行计算。 下面两张图是对婚姻和年收入的详细计算过程&#xff08;为GINI系…

Hive_Hive统计指令analyze table和 describe table

之前在公司内部经常会看到表的元信息的一些统计信息&#xff0c;当时非常好奇是如何做实现的。 现在发现这些信息主要是基于 analyze table 去做统计的&#xff0c;分享给大家 实现的效果某一个表中每个列的空值数量&#xff0c;重复值数量等&#xff0c;平均长度 具体的指令…