C++中string类用法(上)

简介: C++中string类用法(上)

String类

在C语言中,我们对于对于字符数组的认识,仅仅局限于字符串,我们对于该数组进行一些操作的时候,往往要配合str系列的库函数来使用,但是这些库函数比较繁琐,底层空间需要用户来维护,有可能会在操作中越界访问。

C++中提供了string类,来完善对于字符串的使用和处理

总结:

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

String的用法

我们对于String的使用从string的构造开始

string类对象的构造方法

四种构造方法:

1. string( )空参构造,得到一个空的字符串类型

2.string(const char* s) 使用C-string来构造string类对象,即传参一个字符串"xxx"

3.string(size_t n ,char c)表示得到一个n个c字符的字符串

4.string(const string& s)表示通过拷贝构造函数来得到新的string对象

下面是代码演示:

int main()
{
    string s1;   //得到空字符串
    string s2("hello world!!!");  //字符串内容为hello world!!!\0
    string s3(s2);  //拷贝构造函数,数值一样,地址不同
    string s4(10,'c');//十个字符为c的连起来的字符串
    return 0;
}

string类的容量操作

函数名称 功能说明
size 返回字符串有效字符长度
length 返回字符串有效字符长度
capacity 返回空间总大小
empty 检测字符串是否为空串,如果为空,返回true,反之返回false
clear 清空字符串
reserve 扩容,为字符串预留足够的空间
resize 设置有效字符的个数,多出来的空间用‘\0’来代替

代码演示

size和length的区别

size()和length()方法底层实现的原理是一样的,只是为了与其他STL容器接口保持一致,才又加入了size(),以后一般情况下,直接使用size()即可

#include<iostream>
#include<string>
using namespace std;  //需要导入这些头文件以及展开std命名空间,下文不再一一写入代码段
void test11()
{
  string s("hello world");
  cout << s.size() << endl; //11
  cout << s.length() << endl;//11
}
int main()
{
  test11();
  return 0;
}

capacity的使用

capacity在不同的环境下的初始化的数值是不同的,在VS下我们根据下面代码可知,初始值为15,后序大概为1.5倍扩容,在Linux下初始值从0开始,。后序大概为2倍扩容。

void test12()
{
  //测试capacity
  String::string s;
  int num = s.capacity();
  cout << num << endl;
  for (int i = 0; i < 100; i++)
  {
    s += i;
    if (num != s.capacity())
    {
      cout << "capacity改为:" << s.capacity() << endl;
    }
    num = s.capacity();
  }
}
int main()
{
  test12();
  return 0;
}

empty和clear的使用

empty是用来判断当前字符串是否为空值(空串),真返回1,假返回0

clear用来清理字符串内容的,实际上是通过'\0'来实现的,会改变size的数值为0,capacity不会改变

void test13()
{
  string s("hello world");
  //检验empty的使用
  cout << s.empty() << endl;
  s.clear(); //当清空clear字符串s之后,再通过empty判空
  cout << s.empty() << endl;
  //empty返回为0表示假,返回为1表示真
  //判断clear之后是否size以及capacity发生改变
  string s1("hello world");
  cout << "当前capacity为:"<<s1.capacity() << endl;
  cout << "当前size为:" <<s1.size() << endl;
  //清理clear
  s1.clear();
  cout << "当前capacity为:" << s1.capacity() << endl;
  cout << "当前size为:" << s1.size() << endl;
}
int main()
{
  test13();
  return 0;
}

reserve和resize的使用

reserve是用来扩容更改capacity的,reserve在VS中只能向上扩容,不能缩容(减小),且不会改变size的大小

resize是用来改变字符串长度大小的,不管如何要满足size<=capacity,所以resize底层会在更改size大小前调用reserve来判断此时是否需要扩容,所以可能会影响capacity的大小

//capacity的大小只能是在15 31 ...这样的取值,保证大概1.5倍扩容,向上扩容
void test14()
{
  string s("hello world");
  cout << "原始数值" << endl;
  cout << "当前capacity为:" << s.capacity() << endl;
  cout << "当前size为:" << s.size() << endl;
  //验证reserve和resize
  s.reserve(20);
  cout << "使用reserve扩容后:" << endl;
  cout << "当前capacity为:" << s.capacity() << endl;
  cout << "当前size为:" << s.size() << endl;
  s.resize(20);
  cout << "使用resize更改size后:" << endl;
  cout << "当前capacity为:" << s.capacity() << endl;
  cout << "当前size为:" << s.size() << endl;
  cout << endl;
  //如果此时我们通过reserve缩容(减小当前容量)
  s.reserve(10000);
  cout << "缩容为10:结果如下" << endl;
  cout << "当前capacity为:" << s.capacity() << endl;
  cout << "当前size为:" << s.size() << endl;
  //那么我们继续缩小size
  s.resize(100);
  cout << "缩小size为5:结果如下" << endl;
  cout << "当前capacity为:" << s.capacity() << endl;
  cout << "当前size为:" << s.size() << endl;
}
int main()
{
  test14();
  return 0;
}

shrink_to_fit()

一般capacity扩容之后,不会再缩容,但是通过shrink_to_fit将容器的内部存储空间缩小到恰好容纳当前元素数量,以节省不必要的内存空间

shrink_to_fit使得capacity==size

总结

  1. size()与length()底层实现是一样的,是因为为了与STL其他容器兼容,所以增加了size()的使用,以后一般使用size()
  2. clear()只是将string中有效字符清空,不会改变底层空间的大小
  3. resize(size_t n) 和resize(size_t n , char c) 都是可以改变size,只是对于多余的空间,前者默认用‘\0’来填充,后者用指定字符c来填充,其他是一致的。
  4. resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。也就是size<=capacity
  5. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

string类对象的访问

我们有四种方法来对于string对象进行访问

函数名称 功能说明
operator[ ] 返回指定位置的字符,const string类对象调用
begin + end begin( )获取string对象的首元素的迭代器+end( )获得最后一个字符的下一个位置的迭代器
rbegin+rend rbegin得到最后一个字符的迭代器+rend得到string对象第一个字符的上一个位置的迭代器
范围for(增强for) 是C++11支持的for循环遍历方式,本质是调用迭代器
void test15()
{ 
  string s("hello world");
  //1.operator[] 访问,可以赋值  和数组一样
  cout << s[0] << endl;
  //2.begin 和 end 的使用   迭代器类似于指针的使用
  string::iterator it = s.begin();
  //auto it =s.begin();   可以用auto智能指针接收
  while(it!=s.end())
  {
    cout << *it << "";
    ++it;
  }
  cout << endl;
  //3.rbegin和end的使用,就是反向遍历
  string::reverse_iterator rit = s.rbegin();
  while (rit != s.rend())
  {
    cout << *rit << "";
    ++rit;   //反向遍历也是要++rit迭代器
  }
  cout << endl;
  //4.范围for
  for (auto ch : s)
  {
    cout << ch << "";  //范围for只能正向遍历
  }
  cout << endl;
}
int main()
{
  test15();
  return 0;
}

注意:反向迭代器和正向迭代器的rit/it都是++

string类对象的修改操作

函数名称 功能说明
push_back 在字符串后尾插字符c
append 在字符串后追加一个字符串str
operator+= 在字符串后追加字符串str
c_str 返回C格式的字符串,也就是const char* (char指针)
find+npos 从字符串pos位置开始向后找字符c,返回该字符在字符串中的位置
rfind 从字符串pos位置开始向前找字符c,返回该字符在字符串中的位置
substr 在str中从pos位置开始,截取n个字符,然后将其返回
insert 在pos指定位置插入字符串str,很多insert用法,常用的如下。
earse 在pos位置开始,删除n个字符

函数的使用

push_back和append以及operator+=

都是在string类对象尾部,添加字符或者字符串,push_back添加字符C,append以及operator+=添加字符串

void test16()
{
  string s;//空串
  //push_back的使用
  s.push_back('1');
  s.push_back('2');
  s.push_back('3');
  s.push_back('4');
  s.push_back('5');
  for (auto ch : s)
  {
    cout << ch << " ";
  }
  cout << endl;
  //append
  s.append("hello world");
  for (auto ch : s)
  {
    cout << ch << " ";
  }
  cout << endl;
  //3.operator+=
  s += "abcdefg";
  for (auto ch : s)
  {
    cout << ch << " ";
  }
}
int main()
{
  test16();
  return 0;
}

相关文章
|
8天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
36 4
|
10天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
33 4
|
1月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
55 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
30天前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
24 2
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
53 1