C++入门7——string类的使用-2

简介: C++入门7——string类的使用-2

4. string类对象的容量操作

4.1 string中有效字符个数(size与length)

size与length返回字符串有效字符长度(不包含\0)

前面已经说过,size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。

代码演示:

int main()
{
  string s1("hello world!");
  cout << s1.size() << endl;
  return 0;
}

4.2 string的当前容量(capacity)

capacity返回空间总大小

代码如下:

int main()
{
  string s1("hello world!");
  cout << s1.capacity() << endl;
  return 0;
}

设计程序检测string的扩容机制:

int main()
{
  string s1("hello world!");
  //检测string的扩容机制
  size_t old = s1.capacity();    //令old=原本的容量大小
  cout << old << endl;        
  for (size_t i = 0; i < 500; i++)
  {
    s1.push_back('l');         //尾插l的过程中string的容量大小一定会发生变化
    if (old != s1.capacity())  //当发生扩容时,打印新的string容量大小
    {
      cout << s1.capacity() << endl;
      old = s1.capacity();
    }
  }
  return 0;
}

4.3 string提前开空间(reserve)

reserve为字符串预留空间(即可以理解为需要多少空间提前开好,不用边用边开)

reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。(在vs2019下reserve只扩容不缩容,在g++下reserve会缩容,但只会缩到现有数据的大小)

用法如下:

int main()
{
  string s1("hello world!");
  cout << s1.capacity() << endl;
  s1.reserve(500);
  cout << s1.capacity() << endl;
  return 0;
}

4.4 string提前开空间并初始化(resize)

resize将有效字符的个数改变成n个,多出的空间用字符c填充

resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用'\0'来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。

①如果要扩容的空间>capacity,则扩容+尾插。验证如下:

int main()
{
  string s1("hello world!");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
 
  //如果要扩容的空间>capacity
  s1.resize(100);
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  return 0;
}

int main()
{
  string s1("hello world!");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
 
  //如果要扩容的空间>capacity
  s1.resize(100, 'x');
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  return 0;
}

②如果size<n<capacity,则只尾插不扩容,验证如下:

int main()
{
  string s1("hello world!");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
 
  //如果size<n<capacity
  s1.resize(13,'x');
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  return 0;
}

③如果n<size,只删除、保留前n个,不缩容,验证如下:

int main()
{
  string s1("hello world!");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
 
  //如果n<capacity
  s1.resize(6);
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  return 0;
}

(reserve与resize的区别可以概括为一句话:reserve只影响容量,不影响数据,resize既影响容量又影响数据)

4.5 清空string所有字符(clear)

清空有效字符

clear()只是将string中有效字符清空,不改变底层空间大小。

验证如下:

int main()
{
  string s1("hello world!");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
 
  s1.clear();
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  return 0;
}

4.6 判断string是否为空(empty)

检测字符串释放为空串,是返回true,否则返回false(注意:空为真,不空为假)

验证如下:

int main()
{
  string s1("hello world!");
  cout << s1.empty() << endl;
  string s2;
  cout << s2.empty() << endl;
  return 0;
}

5.string类对象的修改操作

修改操作无非就是增删查改,这里只介绍比较常用的操作

5.1 增

1. append插入

增操作其实已经有了一个我们熟悉的push_back,可是push_back每次只能尾插一个字符,为了方便就有了append:

比如我们二者兼用:

int main()
{
  string s1("hello");
  s1.push_back(' ');
  s1.append("world!");
  cout << s1 << endl;
  return 0;
}

append的其他常用用法如下:

①string& append (const char* s);插入常量字符串(上面的用法)

②string& append (const string& str);插入string:

int main()
{
  string s1("hello");
  string s2(s1);
  return 0;
}

③string& append (const string& str, size_t subpos, size_t sublen);插入string从subpos位置起到sublen个字符止的那部分:

int main()
{
  string s1("hello world!");
  string s2(s1,1,3);
  return 0;
}

④string& append (const char* s, size_t n);插入常量字符串的前n个:

int main()
{
  string s1("hello world!", 3);
  return 0;
}

⑥string& append (size_t n, char c);插入n个字符c:

int main()
{
  string s1(4,'x');
  return 0;
}

⑦string& append (InputIterator first, InputIterator last);插入string的一部分:

插入s1:

int main()
{
  string s1("hello world!");
  string s2;
  s2.append(s1.begin(), s1.end());
  return 0;
}

插入去头去尾的s1:

int main()
{
  string s1("hello world!");
  string s2;
  s2.append(++s1.begin(), --s1.end());
  return 0;
}

2. +=插入

①string& operator+= (const string& str);插入string:

int main()
{
  string s1("hello world!");
  string s2;
  s2 += s1;
  return 0;
}

②string& operator+= (const char* s);插入字符串:

int main()
{
  string s1;
  s1 += "hello world!";
  return 0;
}

③string& operator+= (char c);插入字符:

int main()
{
  string s1;
  s1 += '!';
  return 0;
}

3.insert指定位置插入

观察push_back与append,二者都是尾插,那有没有不是尾插的方法呢?当然有!

①string& insert (size_t pos, const string& str);在pos位置插入string:

int main()
{
  string s1("hello ");
  string s2("world!");
  s2.insert(0, s1);
  return 0;
}

②string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);在pos位置插入string的一部分:

int main()
{
  string s1("hello ");
  string s2("world!");
  s2.insert(0, s1, 1, 3);//ellworld!
  return 0;
}

③string& insert (size_t pos, const char* s);在pos位置插入字符串:

int main()
{
  string s1("world!");
  s1.insert(0, "hello ");//hello world!
  return 0;
}

④string& insert (size_t pos, const char* s, size_t n);在pos位置插入字符串的前n个:

int main()
{
  string s1("world!");
  s1.insert(0, "hello ",3);//helworld!
  return 0;
}

5.2 删

1. 尾删(pop_back)

尾删:

int main()
{
  string s1("hello world!");
  s1.pop_back();//hello world
  return 0;
}

2. 删除指定位置(erase)

①string& erase (size_t pos = 0, size_t len = npos);从第pos个位置删除len个字符:

int main()
{
  string s1("hello world!");
  s1.erase(5, 1);//helloworld!
  return 0;
}

②iterator erase (iterator p);删除string的第p个位置:

int main()
{
  string s1("hello world!");
  s1.erase(s1.begin()+1);//hllo world!
  return 0;
}

5.3 查

1.取string子串(substr)

string substr (size_t pos = 0, size_t len = npos) const;从pos位置开始,取len个字符:

int main()
{
  string s1("hello world!");
  string s2 = s1.substr(6, 5);//world
  return 0;
}

2.查找指定字符(find)

关于find的返回值:

The position of the first character of the first match.

If no matches were found, the function returns string::npos.

即:如果找到了就返回找到的第一个的下标,如果没有找到就返回整型的最大值。

①size_t find (char c, size_t pos = 0) const;从pos位置开始找c,没有给pos默认从头开始找:

int main()
{
  string s1("hello world!");
  size_t pos1 = s1.find('l');//2
  size_t pos2 = s1.find('l', 5);//9
  size_t pos3 = s1.find('x');//npos
  return 0;
}

例:取string指定的一部分:

int main()
{
  //取文件名后缀
  string s1("test.cpp");
  size_t pos1 = s1.find('.');
  if (pos1 != string::npos)
  {
    /*string s2 = s1.substr(pos1, s1.size() - pos1);*/
    string s2 = s1.substr(pos1);
    cout << s2 << endl;
  }
  return 0;
}

5.4 改

1. 修改string指定位置(replace)

①string& replace (size_t pos, size_t len, const string& str);在pos位置的len个字符替换成str:

int main()
{
  string s1("hello world!");
  s1.replace(5, 1, "?");//hello?world!
  return 0;
}

2. string与string交换(swap)

与另一个string交换:

int main()
{
  string s1("hello world!");
  string s2;
  s2.swap(s1);//hello world!
  return 0;
}

5.5 查改结合完成替换操作

例:将s1的空格全部替换为?

方法一:

int main()
{
  string s1("have a good time!");
  cout << "替换前s1=" << s1 << endl;//have a good time!
  size_t pos = s1.find(' ', 0);
  while (pos != string::npos)
  {
    s1.replace(pos, 1, "?");
    pos = s1.find(' ', pos + 1);
  }
  cout << "替换后s1=" << s1 << endl;//have?a?good?time!
  return 0;
}

实际中replace效率太低,因此尽量少用replace

方法二:

int main()
{
  string s1("have a good time!");
  cout << "替换前" << s1 << endl;//have a good time!
  string s2;
  for (auto ch : s1)
  {
    if (ch == ' ')
    {
      s2 += "?";
    }
    else
    {
      s2 += ch;
    }
  }
  s1.swap(s2);
  cout << "替换后" << s1 << endl;//have?a?good?time!
  return 0;
}


相关文章
|
5月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
364 5
|
9月前
|
对象存储 C++ 容器
c++的string一键介绍
这篇文章旨在帮助读者回忆如何使用string,并提醒注意事项。它不是一篇详细的功能介绍,而是一篇润色文章。先展示重载函数,如果该函数一笔不可带过,就先展示英文原档(附带翻译),最后展示代码实现与举例可以直接去看英文文档,也可以看本篇文章,但是更建议去看英文原档。那么废话少说直接开始进行挨个介绍。
169 3
|
11月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
425 12
|
9月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
229 0
|
9月前
|
存储 编译器 C语言
关于string的‘\0‘与string,vector构造特点,反迭代器与迭代器类等的讨论
你真的了解string的'\0'么?你知道创建一个string a("abcddddddddddddddddddddddddd", 16);这样的string对象要创建多少个对象么?你知道string与vector进行扩容时进行了怎么的操作么?你知道怎么求Vector 最大 最小值 索引 位置么?
222 0
|
9月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
366 0
|
9月前
|
存储 安全 编译器
c++入门
c++作为面向对象的语言与c的简单区别:c语言作为面向过程的语言还是跟c++有很大的区别的,比如说一个简单的五子棋的实现对于c语言面向过程的设计思路是首先分析解决这个问题的步骤:(1)开始游戏(2)黑子先走(3)绘制画面(4)判断输赢(5)轮到白子(6)绘制画面(7)判断输赢(8)返回步骤(2) (9)输出最后结果。但对于c++就不一样了,在下五子棋的例子中,用面向对象的方法来解决的话,首先将整个五子棋游戏分为三个对象:(1)黑白双方,这两方的行为是一样的。(2)棋盘系统,负责绘制画面。
131 0
|
12月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
12月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
217 16