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;
}


相关文章
|
9天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
36 4
|
10天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
33 4
|
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++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
23 3
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
53 1