为什么要有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默认值为空,其他编译器给的默认值可能是随机值。我们可以直接在拷贝构造函数加一个参数列表,给临时对象赋值为空

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

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

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

    list容器 带头结点的双向循环链表 list操作 list容器的概念及其操作 构造和销毁 list<int>L1;list<int>L2(10, 5);vector<i ...

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

    vector容器 动态的顺序表,数组. vector操作 vector操作及其概念 构造 vector<int>v1;vector<int>v2(10, 5);vector&l ...

  3. EXT核心API详解(二)-Array/Date/Function/Number/String

    EXT核心API详解(二)-Array/Date/Function/Number/String Array类 indexOf( Object o )  Number object是否在数组中,找不到返 ...

  4. 《信息系统项目管理师软考辅导——3年真题详解与全真模拟》主要创新点、关注点...

    <信息系统项目管理师软考辅导--3年真题详解与全真模拟>主要创新点.关注点 新增2014年5月.11月两份真题试卷的360°透彻解析: 更新2013年5月.11月真题试卷的解析: 紧扣考纲 ...

  5. 云原生存储详解:容器存储与 K8s 存储卷

    作者 | 阚俊宝 阿里云技术专家 导读:云原生存储详解系列文章将从云原生存储服务的概念.特点.需求.原理.使用及案例等方面,和大家一起探讨云原生存储技术新的机遇与挑战.本文为该系列文章的第二篇,会对容 ...

  6. docker多个容器一起打包_详解Docker 容器基础系统镜像打包

    因为容器本身是共享宿主操作系统内核,所以容器基础系统镜像包本身就是一个标准的 Linux rootfs + 用户自定义的工具.根据这个思路,我们就可以构建一个自己的容器基础系统镜像. 构建标准的 Li ...

  7. docker修改镜像的存储位置_云原生存储详解:容器存储与 K8s 存储卷(内含赠书福利)...

    作者 | 阚俊宝  阿里巴巴技术专家 参与文末留言互动,即有机会获得赠书福利! 导读:云原生存储详解系列文章将从云原生存储服务的概念.特点.需求.原理.使用及案例等方面,和大家一起探讨云原生存储技术新 ...

  8. 云原生存储详解:容器存储与K8s存储卷

    作者 | 阚俊宝 阿里云技术专家 导读:云原生存储详解系列文章将从云原生存储服务的概念.特点.需求.原理.使用及案例等方面,和大家一起探讨云原生存储技术新的机遇与挑战.本文为该系列文章的第二篇,会对容 ...

  9. 前端中unescape是什么意思_详解JavaScript中的Unescape()和String() 函数

    JavaScript中的Unescape()和String() 函数详解,具体内容如下所示: 定义和用法 JavaScript unescape() 函数可对通过 escape() 编码的字符串进行解 ...

最新文章

  1. 【Python自动化测试】setuptools
  2. matlab求解外弹道,基于MATLAB∕Simulink的通用质点外弹道程序设计.pdf
  3. python电脑配置苹果笔记本-tensorflow学习笔记1——mac开发环境配置
  4. 有关推挽输出、开漏输出、复用开漏输出、复用推挽输出以及上拉输入、下拉输入、浮空输入、模拟输入区别
  5. sringboot security基本用法
  6. iso qemu 安装ubuntu_基于libvirt 和QEMU在macOS安装Ubuntu
  7. JSON.parse 解析json字符串时,遇换行符报错
  8. 写出记录型信号量中的wait操作代码_操作系统进程的同步与互斥及经典同步与互斥问题...
  9. JavaScript解决e6不支持不支持max-width,max-height的问题的方法
  10. 浏览器第二节:TCP协议:如何保证页面文件能被完整送达浏览器
  11. iOS常用三方库、插件、知名技术博客、常用开发工具使用介绍等等,大家可以一次性下载了!
  12. 齐次线性方程组与非齐次线性方程组的区别
  13. 产品读书《科技想要什么》KK
  14. 浏览器首页被恶意篡改
  15. 奇妙的裴波那契数列和黄金分割
  16. python操作符是什么意思_如何使用python操作符**与*?有什么区别?
  17. 《Java多线程编程实战》——第2章 设计模式及其作用
  18. vue下载后端返回的压缩包
  19. cv2 改变图片大小 resize
  20. 算法系列——贝尔曼福特算法(Bellman-Ford)

热门文章

  1. 简单好用的计算器:bc
  2. 1058. 选择题(20)
  3. Android 软键盘自动弹出和关闭
  4. EF Code First 简单的示例
  5. argmax函数_1.4 TensorFlow2.1常用函数
  6. linux上验证cudnn是否安装成功_deepin15.7中安装tensorflow+cuda9.0+cudnn7的步骤
  7. play 连接mysql_Play framework 2.x 连接mysql | 学步园
  8. 华菱重卡仪表指示说明_仪表装置11种常见故障的解决方法
  9. android dropbox anr分析,Android如何分析排查ANR
  10. python画画bup_Python中的高效Vector / Point类