【编码艺术:掌握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;
    }
};
相关文章
|
15天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
26 2
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
62 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
40 2
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
23 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
57 4
|
2月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
28 2
|
2月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
21 0
|
3月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
40 0
java基础(13)String类
|
3月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①
|
2月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
38 4