详解string容器(应用+模拟实现,string练习题)

为什么要有string容器

string:其实就是一个字符串,c++对字符串进行了封装的,封装到一个类里面,这样用户就不用担心开辟空间的问题,只需要往string类里放字符串就可以了,string其实还可以自增长
很多人就会有一个疑问,以前在c语言中,已经有字符串了,其实c语言中表示字符串就是char*,也就是字符数组+'\0'

string应用

string类的应用

string类常用接口

构造

在这里插入图片描述

	string s1;string s2("hello");string s3(10, '$');string s4(s3);

容量

void TestString()
{string s("hello");cout << s.size() << endl;    //size和length都是求字符串的有效个数cout << s.length() << endl;cout << s.capacity() << endl;if (s.empty())cout << "NULL" << endl;elsecout << "NOT NULL string" << endl;//只清空string类中有效字符个数,不会改变底层空间的大小s.clear();		//不会清空容量cout << s.size() << endl;cout << s.capacity() << endl;if (s.empty())cout << "NULL" << endl;elsecout << "NOT NULL string" << endl;
}

在这里插入图片描述

resize()

void resize(size_t n, char ch):功能—将string类中的有效字符改变到n个,多的字符采用ch进行填充
注意

  1. 将string类中有效元素缩小,只改变有效元素的个数,不会改变底层空间的大小
  2. 如果是将string类中有效元素增多,可能需要扩容
void TestString3()
{string s("hello");cout << s << endl;cout << s.size() << endl;cout << s.capacity() << endl;//resize 10个有效字符,原来有5个,多出来的5个用!号进行填充s.resize(10,'!');cout << s << endl;cout << s.size() << endl;cout << s.capacity() << endl;}
reserve()
void reserve(size_t newcapacity)
  1. newcapacity > oldcapacity(string 类旧空间增多---->容量改变(最终容量大小) >= newcapacity)
  2. newcapacity < oldcapacity(string 类旧空间缩小---->该函数直接返回,除非newcapacity < 15,缩小到15
    注意:
    只改变容量大小,不会改变有效元素个数
void TestString4()
{string s("hello");cout << s.size() << endl;cout << s.capacity() << endl;s.reserve(20);cout << s.size() << endl;cout << s.capacity() << endl;s.reserve(40);cout << s.size() << endl;cout << s.capacity() << endl;s.reserve(24);cout << s.size() << endl;cout << s.capacity() << endl;s.reserve(5);cout << s.size() << endl;cout << s.capacity() << endl;
}

在这里插入图片描述
string类维护了一个空间16字节

元素访问

在这里插入图片描述

void TestString5()
{string s("hello");cout << s[0] << endl;s[0] = 'H';//[] 如果越界----assert触发cout << s.at(2) << endl;s.at(2) = 'L';//s.at()如果越界---抛出out_of_range的异常//不同点//cout << s[10] << endl;  //越界访问cout << s.at(10) << endl;
}
cout << s[10] << endl;  //越界访问

在这里插入图片描述

cout << s.at(10) << endl;//越界访问

在这里插入图片描述

元素修改

在这里插入图片描述

void TestString6()
{string s1;s1.push_back('I');s1 += " Love ";string s2("you");s1 += s2;s1.append(1, ',');s1.append("祖国");s1.append(3, '!');cout << s1 << endl;
}

在这里插入图片描述

string类扩容机制
  1. vs—PJ----1.5倍扩容
  2. Linux-----SGI----2倍扩容
  3. 如果提前直到大概要往string类存放多少个元素,可以通过reserve将空间给好
void TestPushBack() 
{ string s;       //维护一个数组,最多放15个有效字符size_t sz = s.capacity();    cout << "making s grow:\n";    for (int i = 0; i < 100; ++i)    { s.push_back( 'c');        if (sz != s.capacity())        //sz记录上一次容量大小{ sz = s.capacity();            cout << "capacity changed: " << sz << '\n'; } } 
}

在这里插入图片描述

字符串特殊操作

在这里插入图片描述

void TestString7()
{string s("123456");int ret = atoi(s.c_str());}//find rfind
void TestString8()
{string s("hello world");size_t pos = s.find( ' ');if (pos != string::npos){cout << ' ' << "is in s"<<endl;}pos = s.find("world");if (pos != string::npos){cout << "world" << "is in s" << endl;}//获取文件后缀string ss("2019-10-26.cpp.cpp");pos = ss.rfind('.') + 1;  //后缀的位置从.的后面开始cout << pos << endl;string filepos = ss.substr(pos);cout << filepos << endl;}

在这里插入图片描述

迭代器(string中很少用到)

三种遍历方法
void TestString9()
{string s("hello");for (auto e : s){cout << e;}cout << endl;for (int i = 0; i < s.size(); ++i)cout << s[i];cout << endl;//char *string::iterator it = s.begin();while (it!=s.end()){cout << *it ;++it;}cout << endl;
}

反转字符串

一个函数调用就可以

//反转字符串
void reversestring(string &s)
{//char* begin = (char *)s.c_str();//char* end = begin + s.size() - 1;//while (begin < end)//{//	swap(*begin, *end);//	begin++;//	end--;//}reverse(s.begin(), s.end());
}

字符串中第一个唯一字符

字符做为数组下标

class Solution {
public:int firstUniqChar(string s) {//1.统计每个字符出现的次数int count[256]={0};for(auto e:s){count[e]++;}//2.找第一个只出现一次的字符for(size_t i = 0; i < s.size(); ++i){if(count[s[i]] == 1)return i;}return -1;}
};

字符串最后一个单词的长度

cin用来接受字符串,如果字符串中出现空格,换行,是无法拿到整个字符串的

#include<iostream>
#include<string>
using namespace std;int main()
{string s;getline(cin,s);//找到最后一个单词的位置cout<< s.substr(s.rfind(' ')+1).size();return 0;
}

字符串相加

在这里插入图片描述
大数相加,无法用普通的类型进行保存
可以把这些数据看成是字符串

class Solution {
public:string addStrings(string num1, string num2) {int LSize = num1.size();        int RSize = num2.size();                // 以长字符串作为外部循环        if(LSize < RSize)        {            num1.swap(num2);            swap(LSize, RSize);        } string strRet;        strRet.reserve(LSize+1);                char cRet = 0;        char cstep = 0;for(size_t i = 0; i < LSize; ++i)  {            cRet = num1[LSize - i - 1] - '0' + cstep;            cstep = 0;                        if(i < RSize)            {                cRet += num2[RSize - i - 1] - '0';           }                       if(cRet >= 10)            {               cRet -= 10;               cstep = 1;           }                       strRet += cRet + '0';        } if(cstep)            strRet += '1';                reverse(strRet.begin(), strRet.end());                return strRet;    } 
};

string模拟实现

string模拟实现

如何实现

string是动态管理字符串,不论多少个数,都可以管理,编译器中会有一个静态数组,刚开始大小为16字节,有效元素个数为15个,所以当有效字符小于15个时,直接用静态数组存放,效率高一些


我们实现的string只给出动态数组,维护一个char*指针,创建对象开辟空间即可

构造

有参构造
用户在创建对象时,一般有参的话,都会给出c风格的字符串,所以我们的参数,就是一个char*的指针,最好设个默认值为空。然后在有参构造里做两件事情,一个是申请空间,另外一个就是拷贝数据
但是要注意用户一般很傻,有可能会传一个空的指针进来,会引起代码崩溃。所以我们一定要对传来的数据进行判空处理,如果为空,则初始化为空串即可。

string(char *str = ""){//如果指针为空,则初始化位空字符串if (nullptr == str)str = "";//申请空间_str = new char[strlen(str) + 1];//strcpy(_str, str);}

拷贝构造
系统会给出默认的拷贝构造,但是存在浅拷贝问题
什么是浅拷贝?
浅拷贝可能通过默认的拷贝构造发生,也有可能通过编译器默认的赋值运算符的重载发生。
所以我们在拷贝构造和赋值时,让每个对象都要拥有一份独立的资源

string(const string& s):_str(new char[strlen(s._str)+1])  //开辟空间{strcpy(_str, s._str);}

赋值运算符重载

  1. 开辟新空间
  2. 拷贝元素
  3. 释放旧空间
  4. 指向新空间
string& operator=(const string& s){//自己给自己赋值,不用做任何操作if (this != &s){//不是自己给自己赋值//1.申请新空间char *temp = new char[strlen(s._str) + 1];//2.拷贝数据strcpy(temp, s._str);//3.释放原来空间delete[]_str;//4.原空间指针指向新空间_str = temp;}return *this;}

析构

看一下当前对象指针有没有资源,如果有,释放掉,随后指针指向空,防止野指针。

~string(){if (_str)delete[]_str;_str = nullptr;}

以上的是模拟实现的一个string的版本

另外一个版本实现string

资源的转移

namespace bite
{class string{public:string(char *str = ""){//如果指针为空,则初始化位空字符串if (nullptr == str)str = "";//申请空间_str = new char[strlen(str) + 1];strcpy(_str, str);}string(const string& s):_str(nullptr){string strTemp(s._str);swap(_str, strTemp._str);}string& operator=(const string& s){//自己给自己赋值,不用做任何操作if (this != &s){string strTemp(s._str);swap(_str, strTemp._str);}return *this;}~string(){if (_str)delete[]_str;_str = nullptr;}//编译器生成的默认赋值运算符重载存在浅拷贝,而且会有资源泄露,没有释放资源private:char * _str;};}

此版本有一个缺陷,就是创立的临时对象的地址不为空,则会引起代码崩溃,在vs2013编译器下这个样实现的string没有问题,因为vs2013默认值为空,其他编译器给的默认值可能是随机值。我们可以直接在拷贝构造函数加一个参数列表,给临时对象赋值为空

还有一种引用计数的方式,采用引用计数来解决浅拷贝的问题
写时拷贝

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

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

相关文章

浅拷贝+引用计数--写时拷贝---模拟实现string容器

引用计数 深拷贝 多个对象共享同一份资源时&#xff0c;最后能够保证该资源只被释放一次 应该由哪个对象释放资源&#xff1f; 由最后一个使用该资源的对象去释放 怎么知道一个对象是最后一个使用该资源的对象&#xff1f; 给一个计数&#xff0c;记录使用该资源对象的个数 实…

详解vector容器(应用+模拟实现,vector相关练习题)

vector容器 动态的顺序表&#xff0c;数组。 vector操作 vector操作及其概念 构造 vector<int>v1;vector<int>v2(10, 5);vector<int>v3(v2);int array[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };vector<int>v4(array, array sizeof(array) / sizeof(a…

详解list容器(应用+模拟实现)

list容器 带头结点的双向循环链表 list操作 list容器的概念及其操作 构造和销毁 list<int>L1;list<int>L2(10, 5);vector<int>v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };list<int>L3(v.begin(), v.end());list<int>L4(L3);元素访问 cout << L3.…

vector和list容器有哪些区别

这个问题的本质还是在问顺序表和链表的区别 底层结构不同 vector容器list容器一段连续的空间带头结点的双向循环链表 元素访问方式 vector容器list容器支持随机访问—O(1)不支持随机访问—O(N)需要扩容不需要扩容任意位置插入元素----O(N)–搬移元素O(1) 迭代器不同 vector…

复习栈和队列,详解最小栈,栈的弹出压入序列,逆波兰表达式求值

栈和队列的概念 栈:吃进去吐出来 对列&#xff1a;吃进去拉出来 数据结构中的栈和内存中的区别 数据结构中的栈具有后进先出的特性&#xff0c;而内存中的栈是一个内存空间&#xff0c;只不过这个内存空间具与数据结构的栈具有相同的特性。 栈和队列操作 栈和队列基本操作…

详解优先级队列priority_queue(应用+模拟实现)

优先级队列的概念 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的此上下文类似于堆&#xff0c;在堆中可以随时插入元素&#xff0c;并且只能检索最大堆元素(优先队列中位于顶部的元 素)。优先队列被实现为容…

私人博客定制

项目背景 可行性方面 需求分析&#xff1a; 详细设计&#xff1a; 数据库设计 博客管理API的设计 标签相关API 服务器端的实现 对数据库操作进行封装 对服务器操作进行封装 客户端实现 具体操作 使用markdown 具体实现 测试 项目效果展示 维护 完整代码 项目…

初识c++中的函数模板

函数模板 函数模板概念 函数模板:编译器生成代码的一个规则。函数模板代表了一个函数家族&#xff0c;该函数模板与类型无关&#xff0c;在使用时被参数化&#xff0c;根据实参类型产生函数的特定类型版本。 函数模板格式 //要让这个函数与类型无关 //Add函数模板 template…

深入理解c++中的函数模板

非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常量来使…

c++中的IO流

c语言中的IO操作 标准类型的输入输出: 输入------>数据来源是通过键盘进行输入输出------>程序中的数据输出到控制台 c语言中: scanf:输入 printf:输出 两个函数的相同点 1 —格式串 2 —不定参数 两个函数的缺陷 1 —用户要提供数据的格式—用户要记忆大量的格式串—…

201301 JAVA2~3级---走格子

请编写一个函数&#xff08;允许增加子函数&#xff09;&#xff0c;计算n x m的棋盘格子&#xff08;n为横向的格子数&#xff0c;m为竖向的格子数&#xff09;沿着各自边缘线从左上角走到右下角&#xff0c;总共有多少种走法&#xff0c;要求不能走回头路&#xff0c;即&…

复习Linux基本操作----常见指令

Linux基本操作 ls命令 ls(list):相当于windows上的文件资源管理器 语法&#xff1a; ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0c;将列出文件名以及其他信息。 常用选项&#xff1a; -a 列出目…

复习Linux基础操作---权限操作

shell命令以及运行原理 Linux严格意义上说的是一个操作系统&#xff0c;我们称之为“核心&#xff08;kernel&#xff09;“ &#xff0c;但我们一般用户&#xff0c;不能直接使用kernel。而是通过kernel的“外壳”程序&#xff0c;也就是所谓的shell&#xff0c;来与kernel沟…

【剑指offer】_01 (二维数组中的查找)

题目描述 在一个二维数组中&#xff08;每个一维数组的长度相同&#xff09;&#xff0c;每一行都按照从左到右递增的顺序排序&#xff0c;每一列都按照从上到下递增的顺序排序。请完成一个函数&#xff0c;输入这样的一个二维数组和一个整数&#xff0c;判断数组中是否含有该…

再谈c++中的多态

何为多态 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 多态的实现 在继承的体系下 基类中必须有虚函数(被virtual关键字修饰的成员函数)&#xff0c;在派生类中必须…

再谈c++中的继承

继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结构&#xff0c;体现了…

红黑树概念及其相关操作的实现

红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但它并不像AVL树一样&#xff0c;每个结点绑定一个平衡因子。但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过 对任何一条从根到叶子的路径上各个结点着色方式的限制&#xff0c…

模拟实现STL中map和set容器

红黑树的迭代器 //红黑树的迭代器 template<class T> struct RBTreeIterator {typedef RBTreeNode<T>Node;typedef RBTreeIterator<T> Self; public:RBTreeIterator(Node* pNode nullptr):_pNode(pNode){}//具有指针操作T& operator*(){return _pNode-…

排序上---(排序概念,常见排序算法,直接插入,希尔排序,直接选择排序,堆排序)

排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&…

排序下---(冒泡排序,快速排序,快速排序优化,快速排序非递归,归并排序,计数排序)

排序上 排序上 交换类排序 基本思想&#xff1a;所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键值较大的记录向序列的尾部移动&#xff0c;键值较小的记录向序列的前部移动。 冒泡…