【C++初阶】string类(一):从基础到实战

🎬 博主名称键盘敲碎了雾霭
🔥 个人专栏 : 《C语言》《数据结构》 《C++》

⛺️指尖敲代码,雾霭皆可破


文章目录

  • 一、string类介绍
  • 二、auto关键字
    • [2.1 特点](#2.1 特点)
    • [2.2 范围for](#2.2 范围for)
  • [三、 string类的常用接口](#三、 string类的常用接口)
    • [3.1 常见构造(constructor)函数](#3.1 常见构造(constructor)函数)
    • [3.2 迭代器](#3.2 迭代器)
    • [3.3 遍历string的方法](#3.3 遍历string的方法)
    • [3.4 容量](#3.4 容量)
      • [3.4.1 **VS的扩容方式**](#3.4.1 VS的扩容方式)
      • [3.4.2 reserve](#3.4.2 reserve)
      • [3.5 其他接口](#3.5 其他接口)
    • [3.5 元素访问](#3.5 元素访问)
  • 四、练习
    • [4.1 仅仅反转字母](#4.1 仅仅反转字母)
    • [4.2 字符串中的第一个唯一字符](#4.2 字符串中的第一个唯一字符)
    • [4.3 验证一个字符串是否是回文串](#4.3 验证一个字符串是否是回文串)
    • [4.4 字符串相加](#4.4 字符串相加)
  • 文章结语

一、string类介绍

C语言中,字符串是以\O'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。(简单来说,字符串用类管理方便一写)

string类的文档介绍

在使用string类时,必须包含#include头文件以及using namespace std;

二、auto关键字

2.1 特点

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得

  • 可以用来简化长类型
cpp 复制代码
	map<string, string> dit;
	//map<string, string>::iterator mit = dit.begin();
	cout << typeid(dit).name() << endl;

	auto mit = dit.begin();
  • 必须具有初始值设定项
cpp 复制代码
	//auto e;//必须使其初始化,
  • 数组不能具有其中包含"auto"的元素类型
cpp 复制代码
数组不能具有其中包含"auto"的元素类型
  • auto不能作为函数的参数,可以做返回值,但是建议谨慎使用(不方便,需要一层一层来看)
cpp 复制代码
void fuc(auto x)
{

}

2.2 范围for

对于一个有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号":"分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。

  • 范围for可以作用到数组和容器对象上进行遍历
cpp 复制代码
	int arr[] = { 1,2,3,4,5,6,7,8 };
	for (auto a : arr)//要改变值加引用
	{
		cout << a << " ";
	}
	cout << endl;
  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

三、 string类的常用接口

3.1 常见构造(constructor)函数

cpp 复制代码
	string s1;
	string s2("hellow world");
	string s3(s2);
	string s4(s2, 5, 6);
	string s5(s2, 2);
	string s6(6, 'X');

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	cout << s5 << endl;
	cout << s6 << endl;
	s2[0] = 'x';
	cout << s2 << endl;
	s2[19];//会报错,用于检查

3.2 迭代器

  • 正向迭代器
cpp 复制代码
	string s1("hellow word");
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
  • 反向迭代器
cpp 复制代码
	string::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
  • const修饰正向迭代器 (也可以用cbegincend)
cpp 复制代码
	string s2("hahahaha");
	string::const_iterator cit = s2.begin();
	while (cit != s2.end())
	{
		//*cit += 2;//不能修改
		cout << *cit << " ";
		cit++;
	}
	cout << endl;
  • const修饰反向向迭代器 (也可以用crbegincrend)
cpp 复制代码
	string::const_reverse_iterator rcit = s2.rbegin();
	while (rcit != s2.rend())
	{
		//*cit += 2;//不能修改
		cout << *rcit << " ";
		rcit++;
	}
	cout << endl;

3.3 遍历string的方法

  • 下标
cpp 复制代码
int main()
{
	string s1("hellow world");
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
}
  • 迭代器
cpp 复制代码
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		//*it-=2;//可以修改
		cout << *it << " ";
		it++;
	}
	cout << endl;
  • 范围for(底层也是迭代器)
cpp 复制代码
	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;

注意范围for必须加引用才能修改

cpp 复制代码
	for (auto& ch : s1)//不加引用没法修改,如果对象比较大也要加上引用
	{
		ch -= 2;
		cout << ch << " ";
	}
	cout << endl;
	cout << s1 << endl;

3.4 容量

注意:容量的大小都不包含'\0'

cpp 复制代码
	string s1("haha");
	cout << s1.size() << endl;
	cout << s1.length() << endl;
	cout << s1.max_size() << endl;//所存储的最大容量

3.4.1 VS的扩容方式

cpp 复制代码
	string s;
	size_t sz = s.capacity();
	cout << sz << endl;
	for (int i = 0; i < 100; i++)
	{
		s.push_back('a');
		if (s.capacity() != sz)
		{
			sz = s.capacity();
			cout << sz << endl;
		}
	}

运行结果

一开始是二倍扩容的,后面是1.5倍扩容,因为第一次并不是在堆上开辟(存在buff数组,后面才存放在堆上),这个跟平台有关

3.4.2 reserve

当提前知道数量,有没有能提前扩容的?答案是有的

cpp 复制代码
int main()
{

	string s;
	s.reserve(120);//编译器会整数倍对齐,使其比100大,没有扩容
	size_t sz = s.capacity();
	cout << sz << endl;
	for (int i = 0; i < 100; i++)
	{
		s.push_back('a');
		if (s.capacity() != sz)
		{
			sz = s.capacity();
			cout << sz << endl;
		}
	}
	return 0;
}

reserve不会缩容,不会影响长度和修改内容,这个也与平台有关,Linux上不同

cpp 复制代码
	string s1("hellow world !!!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << endl;

	s1.reserve(5);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << endl;

	s1.reserve(20);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << endl;

	s1.reserve(32);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << endl

3.5 其他接口

clear函数不清容量

cpp 复制代码
	string s1("hellow world !!!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << endl;

	s1.clear();
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << endl;

clear判断是不是空

cpp 复制代码
	cout << s1.empty() << endl;

shrink_to_fit是一种缩容请求,具体看编译器是否实现

3.5 元素访问


[]at的区别,下标访问报错返回assert断言,而at会抛出异常

四、练习

4.1 仅仅反转字母

仅仅反转字母

题目描述:给你一个字符串s,根据下述规则反转字符串:所有非英文字母保留在原有位置。所有英文字母(小写或大写)位置反转。返回反转后的s

  • 下标法
cpp 复制代码
class Solution {
public:
    bool Check(string s1,int n)
    {
        if(isalpha(s1[n]))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    string reverseOnlyLetters(string s) 
    {
        int n=s.size();
        int left=0;
        int right=n-1;
        while(left<right)
        {
            while(!Check(s,left)&&left<right)
            {
                left++;
            }
            while(!Check(s,right)&&left<right)
            {
                right--;
            }
            swap(s[left++],s[right--]);
        }
        return s;
    }
};
  • 迭代器(双指针法)
cpp 复制代码
class Solution 
{
public:
    bool Check(char ch)
    {
        if(isalpha(ch))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    string reverseOnlyLetters(string s) 
    {
        string::iterator left=s.begin();
        string::iterator right=s.end()-1;
        while(left<right)
        {
            while(!Check(*left)&&left<right)
            {
                left++;
            }
            while(!Check(*right)&&left<right)
            {
                right--;
            }
            swap(*left++,*right--);
        }
        return s;
    }
};

4.2 字符串中的第一个唯一字符

字符串中的第一个唯一字符
题目描述:给定一个字符串s,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回-1

cpp 复制代码
class Solution {
public:
    int firstUniqChar(string s) 
    {
        int count[26]={0};
        for(auto ch:s)
        {
            count[ch-'a']++;
        }
        for(size_t i=0;i<s.size();i++)
        {
            if(count[s[i]-'a']==1)
            {
                return i;
            }
        }
        return -1;
    }
};

4.3 验证一个字符串是否是回文串

验证一个字符串是否是回文
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个回串。字母和数字都属于字母数字字符。给你一个字符串s,如果它是回文串,返回true;否则,返回false

cpp 复制代码
class Solution 
{
public:
    bool Check(char ch)
    {
        return isalpha(ch)||isdigit(ch);
    }
    bool isPalindrome(string s) 
    {
        for(auto& ch:s)
        {
            if(Check(ch))
            {
                if(ch>='A'&&ch<='Z')
                {
                    ch+=32;
                }
            }
        }
        auto left=s.begin();
        auto right=s.end()-1;
        while(left<right)
        {
            while(left<right&&!Check(*left))
            {
                left++;
            }
            while(left<right&&!Check(*right))
            {
                right--;
            }
            if(*left!=*right)
            {
                return false;
            }
            left++;
            right--;

        }
        return true;
    }
};

4.4 字符串相加

题目描述:给定两个字符串形式的非负整数num1 和num2,计算它们的和并同样以字符串形式返回。你不能使任何內建的于处理整数的库(比如 BigInteger),也不能直接将输入的字符串转换为整数形式。

cpp 复制代码
class Solution 
{
public:
    string addStrings(string num1, string num2) 
    {
        int end1=num1.size()-1;
        int end2=num2.size()-1;
        int next=0;
        string s1;
        while(end1>=0||end2>=0)
        {
            int val1=end1>=0?num1[end1--]-'0':0;
            int val2=end2>=0?num2[end2--]-'0':0;
            int ret=val1+val2+next;
            next=ret/10;
            s1.insert(s1.begin(),ret%10+'0');
        }
        if(next!=0)
        {
            s1.insert(s1.begin(),'1');
        }
        return s1;
    }
};

文章结语

感谢你读到这里~我是「键盘敲碎了雾霭」,愿这篇文字帮你敲开了技术里的小迷雾 💻

如果内容对你有一点点帮助,不妨给个暖心三连吧👇

👍 点赞 | ❤️ 收藏 | ⭐ 关注

(听说三连的小伙伴,代码一次编译过,bug绕着走~)

你的支持,就是我继续敲碎技术雾霭的最大动力 🚀

🐶 小彩蛋:

复制代码
      /^ ^\
     / 0 0 \
     V\ Y /V
      / - \
    /    |
   V__) ||

摸一摸毛茸茸的小狗,赶走所有疲惫和bug~我们下篇见 ✨

相关推荐
计算机安禾2 小时前
【数据结构与算法】第15篇:队列(二):链式队列的实现与应用
c语言·开发语言·数据结构·c++·学习·算法·visual studio
Leventure_轩先生2 小时前
[RL]强化学习指导搭建IC2E核反应堆
开发语言·php
迷途之人不知返2 小时前
初次学习模板
c++
程序猿追2 小时前
HarmonyOS 6.0 实战:用 Native C++ NDK 开发一款本地计步器应用
c++·华为·harmonyos
zzginfo2 小时前
var、let、const、无申明 四种变量在赋值前,使用的情况
开发语言·前端·javascript
Q741_1472 小时前
每日一题 力扣 2840. 判断通过操作能否让字符串相等 II 力扣 2839. 判断通过操作能否让字符串相等 I 找规律 字符串 C++ 题解
c++·算法·leetcode·力扣·数组·找规律
kyle~2 小时前
ROS2 ---- TF2坐标变换(1.动态、静态发布,2.缓存,3.监听)
c++·机器人·ros2
csdn_aspnet2 小时前
C++ 求n边凸多边形的对角线数量(Find number of diagonals in n sided convex polygon)
开发语言·c++·算法