【编码艺术:掌握String类函数接口的妙用指南】(四)

简介: 【编码艺术:掌握String类函数接口的妙用指南】

【编码艺术:掌握String类函数接口的妙用指南】(三):https://developer.aliyun.com/article/1425652


6.5.string::c_str:返回一个指向数组的指针,该数组包含表示字符串对象当前值的以 null 结尾的字符序列(即,一个 C 字符串)。


int main()
{
  string s1 = "Sample string";
  char* s2 = new char[s1.length() + 1];
  //char * strcpy ( char * destination, const char * source );
  //strcpy(s2, s1);//参数类型不匹配
  //const char* c_str() const;//返回值为const char*
  strcpy(s2, s1.c_str());
    delete[] s2;
  return 0;
}


7. string类非成员函数




这里为什么实现了两种参数不同的方式?


 这两个重载的 operator+ 函数是为了提供更大的灵活性,使得在字符串的拼接操作中更方便使用不同类型的参数。这是一种 C++ 中的重载技术,允许相同的操作符在不同的情境下具有不同的行为。


让我们分析这两个重载函数:


  1. string operator+ (const string& lhs, const char* rhs);


  • 允许将一个 std::string 对象和一个 C 字符串(以 const char* 表示)进行拼接。
  • 这使得你可以直接将一个 C 字符串连接到一个 std::string 对象的末尾。


  1. string operator+ (const char* lhs, const string& rhs);


  • 允许将一个 C 字符串和一个 std::string 对象进行拼接。
  • 这使得你可以直接将一个 std::string 对象连接到一个 C 字符串的末尾。


这两种方式的存在是为了方便用户在不同的场景下进行字符串的拼接,无论是从 std::string 到 C 字符串,还是从 C 字符串到 std::string


7.1.getline (string):从输入流 is 中提取字符并将它们存储到字符串 str 中,直到找到分隔字符 delim(或者对于 (2) 来说是换行符 '\n')。


流插入字符输入字符时默认以空格或者换行结束。


如果不想以空格作为字符输出结束的标志,就要使用getline().


上面的几个接口了解一下,下面的OJ题目中会有一些体现他们的使用。string类中还有一些其他的 操作,这里不一一列举,在需要用到时不明白了查文档即可。


7.2. vs和g++下string结构的说明


我们先来看一下下面的代码会输出上面呢?

int main()
{
  //sizeof计算的是一个类的大小
  //和空间上存放的数据没有关系
  string str1;
  cout << sizeof(str1) << endl;
  string str2("test string");
  cout << sizeof(str2) << endl;
  return 0;
}


我们先来分析一下


32位平台下大小应该是4+4+4=12字节。我们来看一下程序的输出结果


为什么是28呢?我们来解释一下。


注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。


  • vs下string的结构 string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义
  • string中字 符串的存储空间: 当字符串长度小于16时,使用内部固定的字符数组来存放 当字符串长度大于等于16时,从堆上开辟空间
  • 当字符串长度小于16时,使用内部固定的字符数组来存放 -  _Buf
  • 当字符串长度大于等于16时,从堆上开辟空间 - _Ptr
union _Bxty
{ 
    // storage for small buffer or pointer to larger one
    value_type _Buf[_BUF_SIZE];
    pointer _Ptr;
    char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;


这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内 部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。


其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量


最后:还有一个指针做一些其他事情。


故总共占16+4+4+4=28个字节。


Linux下面就只存放了一个指针,我们可以试一下Linux下的大小。


不是说存放的指针吗?为什么这里是8个字节,因为Linux下是64位平台,具体指针存放的内容是什么呢?


7.3Linxu下指针指向的内容是字符串,指针向前减四字节就是引用计数,再向前减四个字节就是就是容量capacity,再减四个字节就是大小size。


7.3.1.引用计数


这里为什么要这样设置,我们先来看一下传值返回


如果让它指向ret就发生了浅拷贝。


引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给 计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该 对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。这里就解决了析构函数的析构两次出错的问题,对于浅拷贝的另一个问题,需要写时拷贝解决。


7.3.2.写时拷贝

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的,这里的意义在于如果拷贝后不修改内容,那就赚啦。写时拷贝当引用计数为1时才会尝试修改数据时,才会进行实际的复制,以确保独立的修改。


写时拷贝


写时拷贝在读取是的缺陷


8.小试牛刀


仅仅反转字母

class Solution {
public:
  bool isLetter(char ch)
  {
    if (ch >= 'a' && ch <= 'z')
      return true;
    if (ch >= 'A' && ch <= 'Z')
      return true;
    return false;
  }
  string reverseOnlyLetters(string S)
  {
    if (S.empty())
      return S;
    size_t begin = 0, end = S.size() - 1;
    while (begin < end)
    {
      while (begin < end && !isLetter(S[begin]))
        ++begin;
      while (begin < end && !isLetter(S[end]))
        --end;
      swap(S[begin], S[end]);
      ++begin;
      --end;
    }
    return S;
  }
};


找字符串中第一个只出现一次的字符  

class Solution{
public:
 int firstUniqChar(string s) {
   // 统计每个字符出现的次数
   int count[256] = {0};
   int size = s.size();
   for (int i = 0; i < size; ++i)
   count[s[i]] += 1;
   // 按照字符次序从前往后找只出现一次的字符
   for (int i = 0; i < size; ++i)
   if (1 == count[s[i]])
   return i;
   return -1;
   }
};


字符串里面最后一个单词的长度  

#include<iostream>
#include<string>
using namespace std;
int main()
{
  string line;
  // 不要使用cin>>line,因为会它遇到空格就结束了
  // while(cin>>line)
  while (getline(cin, line))
  {
    size_t pos = line.rfind(' ');
    cout << line.size() - pos - 1 << endl;
  }
  return 0;
}


验证一个字符串是否回文

class Solution {
public:
bool isPalindrome(string s) {
    int begin = 0;
    int end = s.size() - 1;
    while (begin < end)
    {
        while (!isalpha(s[begin]) && !isdigit(s[begin]) && begin < end)
            begin++;
        while (!isalpha(s[end]) && !isdigit(s[end]) && begin < end)
            end--;
        if (s[begin] >= 'A' && s[begin] <= 'Z')
            s[begin] = tolower(s[begin]);
        if (s[end] >= 'A' && s[end] <= 'Z')
            s[end] = tolower(s[end]);
        if (s[begin] != s[end])
            return false;
        if (begin < end)
        {
            ++begin;
            --end;
        }
    }
    return true;
    }
};


字符串相加

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.size() - 1;
        int end2 = num2.size() - 1;
        string str;
        //进位
        int next = 0;
        while(end1 >= 0 || end2 >= 0)
        {
            int value1 = 0,value2 = 0;
            if(end1 >= 0)
                value1 = num1[end1--] - '0';
            if(end2 >= 0)
                value2 = num2[end2--] - '0';
            int addvalue = value1 + value2 + next;
            next = addvalue/10;
            addvalue %= 10;
            str.insert(0,1,addvalue + '0');
        }
        if(next == 1)
        {
            str.insert(0,1,'1');
        }
        return str;
    }
};
相关文章
|
2月前
|
存储 C++ 容器
C++入门指南:string类文档详细解析(非常经典,建议收藏)
C++入门指南:string类文档详细解析(非常经典,建议收藏)
42 0
|
11天前
|
存储 安全 C语言
【C++】string类
【C++】string类
|
存储 编译器 Linux
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
|
13天前
|
编译器 C++
标准库中的string类(上)——“C++”
标准库中的string类(上)——“C++”
|
23天前
|
存储 算法 C语言
【C++初阶】8. STL初阶 + String类
【C++初阶】8. STL初阶 + String类
51 1
|
23天前
|
C语言 C++
【C++初阶】9. string类的模拟实现
【C++初阶】9. string类的模拟实现
38 1
|
2月前
|
存储 编译器 C++
string类的模拟实现
string类的模拟实现
29 0
|
2月前
|
编译器 Linux C++
string类的函数讲解
string类的函数讲解
15 1
|
2月前
|
存储 缓存 编译器
C++:String类的使用
C++:String类的使用
|
2月前
|
Java 索引
【Java】String类常用方法总结
【Java】String类常用方法总结
20 0