Обмен технологиями

C Секреты, которые вы не знаете в STL of Heart Resolve (строка)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Оглавление

1. Зачем изучать класс струн?

1.1 Строки на языке C

2. Класс string в стандартной библиотеке.

2.1 строковый класс

2.2 Описание общих интерфейсов класса string

1. Общие конструкции объектов класса строк

2. Операции над объектами класса string

3. Описание структуры строк в vs и g++

3. Реализация моделирования струнного класса

3.2 Мелкая копия

3.3 Глубокое копирование

3.4 Копирование при записи

3.5 Реализация моделирования струнного класса


1. Зачем учитьсянитьдобрый?

1.1 Сстроки в языке

С В языке строки начинаются с '0' Набор символов в конце, для удобства работы, С Стандартная библиотека предоставляет некоторые ул Серия библиотечных функций, но эти библиотечные функции отделены от строк и не соответствуют ООП. мышление, а базовое пространство должно управляться пользователем. Если вы не будете осторожны, вы можете даже получить к нему доступ за пределы.

2. в стандартной библиотекенитьдобрый

2.1 строкадобрый

https://cplusplus.com/reference/string/string/?kw=string

  • 1. String — это класс, представляющий последовательность символов.
  • 2. Поддержка таких объектов обеспечивается стандартным классом строк, интерфейс которого аналогичен интерфейсу стандартного символьного контейнера, но с добавлением специализированных
  • Характеристики проектирования однобайтовых символьных строк.
  • 3. строкакласс используетсимвол(т. е. в качестве типа символа используйте его по умолчаниюchar_traitsи тип распределителя(Дополнительная информация о шаблонах
  • информацию, пожалуйста, посмотритебазовая_строка)
  • 4. строкакласс этобазовая_строкаЭкземпляр класса шаблона, который используетчарсоздавать экземплярбазовая_строкаКласс шаблона и использованиеchar_traits
  • ираспределителькакбазовая_строкаПараметры по умолчанию(Дополнительную информацию о шаблоне см.базовая_строка)
  • 5. Обратите внимание, что этот класс обрабатывает байты независимо от используемой кодировки.:Если используется для обработки многобайтовых символов или символов переменной длины.(нравитьсяUTF-8)последовательность, это
  • все члены класса(например, длина или размер)и его итератор по-прежнему будут в байтах(вместо фактических закодированных символов)действовать.
Подведем итог:
  • 1. строкаЭто строковый класс, представляющий строку
  • 2. Интерфейс этого класса в основном такой же, как и у обычного контейнера, с некоторыми дополнениями, специально используемыми для работы.нитьрутинные операции.
  • 3. строкаДействительно, под капотом это выглядит так:базовая_строкаПсевдоним класса шаблона,typedef basic_string<char, char_traits, allocator>
  • нить;
  • 4. С последовательностями символов, состоящими из нескольких байтов или переменной длины, работать невозможно.
существоватьиспользоватьнитькласс, он должен содержать#включатьзаголовочные файлы, а такжеиспользуя пространство имен std;

2.2 струнаОбщие описания интерфейсов классов

1. нитьОбщие конструкции объектов классов

  1. void Teststring()
  2. {
  3. string s1; // 构造空的string类对象s1
  4. string s2("hello bit"); // 用C格式字符串构造string类对象s2
  5. string s3(s2); // 拷贝构造s3
  6. }

2. нитьОперации с объектами класса

P.S.:

  • 1. размер() идлина()Основной принцип реализации метода точно такой же: вводитсяразмер()Причина в том, чтобы обеспечить совместимость с интерфейсами других контейнеров. Обычно используется size().
  • 2.clear() просто конвертируетнитьДопустимые символы в нем удаляются, а размер основного пространства не изменяется.
  • 3. изменить размер (size_t n) и resize(size_t n, char c)Все они меняют количество допустимых символов в строке нанРазница в том, что при увеличении количества символов: resize(n)использовать0чтобы заполнить дополнительное пространство элемента,resize(size_t n, char c)с персонажамис чтобы заполнить дополнительное пространство элемента. Примечание: изменить размерПри изменении количества элементов, если количество элементов увеличивается, размер базовой емкости может быть изменен. Если количество элементов уменьшено, общий размер базового пространства остается неизменным.
  • 4. резерв(size_t res_arg=0):длянитьЗарезервируйте место и не меняйте количество допустимых элементов.бронироватьпараметры меньше
  • нитьКогда общий размер базового пространства равенрезерваторРазмер емкости не изменится.

3.противиг++ВнизнитьОписание структуры

  Следующая структура находится в 32 Проверьте под платформой, 32 Указатель под битовой платформой занимает 4 байта.
vsdown Структура строки
строка составляет в общей сложности 28 байты , внутренняя структура немного сложнее, во-первых Существует объединение, которое используется для определения Место для хранения строки в строке:
  1. Когда длина строки меньше 16, для ее хранения используется внутренний фиксированный массив символов.
  2. Когда длина строки больше или равна16Когда, открытое пространство из кучи 
  1. union _Bxty
  2. { // storage for small buffer or pointer to larger one
  3. value_type _Buf[_BUF_SIZE];
  4. pointer _Ptr;
  5. char _Alias[_BUF_SIZE]; // to permit aliasing
  6. } _Bx;
Такая конструкция также имеет смысл. В большинстве случаев длина строки меньше. 16 ,Что нить После создания объекта их уже 16. Фиксированное пространство для символьных массивов, отсутствие необходимости создавать его через кучу и высокая эффективность.
Во-вторых: И один размер_т Поле содержит длину строки, a размер_т Поле хранит общую емкость пространства, выделенного в куче.
Наконец: все еще Есть указатель Сделайте что-нибудь еще.
Таким образом, общая сумма16+4+4+4=28байты.
г++ Вниз нить Состав
Г++ Вниз, нить Это реализуется посредством копирования при записи. нить Общее количество объектов 4 Байты, он содержит только указатель, который в будущем будет указывать на кусок кучи, и содержит следующие поля:
  1. общий размер помещения
  2. Эффективная длина строки
  3. Подсчет ссылок
  4. Указатель на пространство кучи, используемое для хранения строк.
  1. struct _Rep_base
  2. {
  3. size_type _M_length;
  4. size_type _M_capacity;
  5. _Atomic_word _M_refcount;
  6. };

3. строкаМакет реализации класса

PS: При самостоятельной реализации класса строки вы должны обратить внимание на проблему поверхностного копирования.

выше Нить Класс явно не определяет свой конструктор копирования и перегрузку оператора присваивания. В это время компилятор синтезирует конструктор по умолчанию. с1 состав делать с2 , компилятор вызовет конструктор копирования по умолчанию. Последняя проблема заключается в том, с1 с2 Совместное использование одного и того же пространства памяти, одного и того же блока при освобождении Пространство освобождается несколько раз, что приводит к сбою программы. , этот метод копирования называется поверхностным копированием.

 3.2 Мелкая копия

Неглубокая копия: также называется битовой копией, компилятор просто копирует значение в объекте. .если Управляйте ресурсами в объектах , и в конце концов это будет в результате чего несколько объектов в сумме При совместном использовании одного и того же ресурса, когда объект уничтожается, ресурс будет освобожден. В это время другие объекты не знают, что ресурс был освобожден, и думают, что ресурс был освобожден. Он по-прежнему действителен, поэтому при продолжении доступа к ресурсу произойдет нарушение прав доступа.

3.3 глубокая копия

Если класс предполагает управление ресурсами, его конструктор копирования, перегрузка оператора присваивания и деструктор должны быть указаны явно.Обычно это обеспечивается в режиме глубокого копирования.

3.4 копирование при записи

Копирование при записи — это своего рода промедление, которое реализуется путем добавления подсчета ссылок на основе поверхностного копирования.
Подсчет ссылок: используется для записи количества пользователей ресурса.Во время строительства количество ресурсов задается как 1 , каждый раз, когда дополнительный объект использует ресурс, счетчик увеличивается на 1 , когда объект уничтожается, сначала уменьшите счетчик 1 , а затем проверьте, нужно ли освободить ресурс, если счетчик равен 1 , указывающий, что объект является последним пользователем ресурса и освобождает ресурс, в противном случае он не может быть освобожден, поскольку есть другие объекты, использующие ресурс;

3,5 струныМакет реализации класса

  1. //string.h
  2. #pragma once
  3. #include<iostream>
  4. #include<assert.h>
  5. using namespace std;
  6. namespace mystr {
  7. class string
  8. {
  9. public:
  10. //迭代器, 因为字符串底层内存连续, 所以可以简单的定义成指针
  11. typedef char* iterator;
  12. typedef const char* const_iterator;
  13. //配合范围for循环
  14. iterator begin() { return _str; }
  15. iterator end() { return _str + _size; }
  16. //兼容常量字符串
  17. const_iterator begin() const { return _str; }
  18. const_iterator end() const { return _str + _size; }
  19. //string();
  20. string(const char* str = "");
  21. string(const string& s);
  22. string& operator=(string temp) { swap(temp); return *this; }
  23. ~string() { delete[] _str; _str = nullptr; _size = _capacity = 0; }
  24. //返回C语言字符数组
  25. const char* c_str() const { return _str; }
  26. size_t size() const { return _size; }
  27. char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; }
  28. const char& operator[](size_t pos) const{ assert(pos < _size); return _str[pos]; }
  29. //重置大小
  30. void reserve(size_t n);
  31. void push_back(char ch) { insert(_size, ch); }
  32. void append(const char* str) { insert(_size, str); }
  33. string& operator+=(char ch) { insert(_size, ch); return *this; }
  34. string& operator+=(const char* str) { insert(_size, str); return *this; };
  35. void insert(size_t pos, char ch);
  36. void insert(size_t pos, const char* str);
  37. void erase(size_t pos = 0, size_t len = npos);
  38. size_t find(char ch, size_t pos = 0) {
  39. for (size_t i = pos; i < _size; i++) if (_str[i] == ch) return i;
  40. return npos;
  41. }
  42. size_t find(const char* str, size_t pos = 0) { return strstr(_str + pos, str) - _str; }
  43. void swap(string& s);
  44. string substr(size_t pos = 0, size_t len = npos);
  45. bool operator<(const string& s) const { return strcmp(_str, s._str) < 0; }
  46. bool operator>(const string& s) const { return !(*this <= s); }
  47. bool operator<=(const string& s) const { return !(*this > s); }
  48. bool operator>=(const string& s) const { return !(*this < s); }
  49. bool operator==(const string& s) const {return strcmp(_str, s._str) == 0; }
  50. bool operator!=(const string& s) const { return !(*this == s); }
  51. void clear() { _str[0] = '0'; _size = 0; }
  52. private:
  53. char* _str;
  54. size_t _size;
  55. size_t _capacity;
  56. //一般static变量的定义要放在类外, 整型是特例
  57. const static size_t npos = -1;
  58. };
  59. void swap(string& s1, string& s2);
  60. istream& operator>>(istream& ci, string& s);
  61. ostream& operator<<(ostream& co, string& s);
  62. }
  1. //string.cpp
  2. #include "string.h"
  3. namespace mystr {
  4. string::string(const char* str):_size(strlen(str)) {
  5. _str = new char[_size + 1];
  6. _capacity = _size;
  7. strcpy(_str, str);
  8. }
  9. string::string(const string& s) {
  10. string temp(s._str);
  11. swap(temp);
  12. }
  13. void string::reserve(size_t n) {
  14. if (_capacity < n) {
  15. char* temp = new char[n + 1];
  16. strcpy(temp, _str);
  17. delete[] _str;
  18. _str = temp;
  19. _capacity = n;
  20. }
  21. }
  22. void string::insert(size_t pos, char ch) {
  23. assert(pos <= _size);
  24. if (_size == _capacity) {
  25. size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
  26. reserve(newcapacity);
  27. }
  28. size_t end = _size + 1;
  29. while (end > pos) _str[end] = _str[end - 1], --end;
  30. _str[pos] = ch;
  31. _size++;
  32. }
  33. void string::insert(size_t pos, const char* str) {
  34. assert(pos <= _size);
  35. size_t len = strlen(str);
  36. if (_size + len > _capacity) reserve(_size + len);
  37. size_t end = _size + len;
  38. while (end > pos + len - 1) _str[end] = _str[end - len], --end;
  39. memcpy(_str + pos, str, len);
  40. _size += len;
  41. }
  42. void string::erase(size_t pos, size_t len) {
  43. if (len > _size - pos) _str[pos] = '0', _size = pos;
  44. else strcpy(_str + pos, _str + pos + len), _size -= len;
  45. }
  46. void string::swap(string& s) {
  47. char* temp = _str;
  48. _str = s._str;
  49. s._str = temp;
  50. std::swap(_size, s._size);
  51. }
  52. string string::substr(size_t pos, size_t len) {
  53. if (len > _size - pos) { string sub(_str + pos); return sub; }
  54. else {
  55. string sub;
  56. sub.reserve(len);
  57. for (size_t i = pos; i < pos + len; i++) sub += _str[i];
  58. return sub;
  59. }
  60. }
  61. void swap(string& s1, string& s2){ s1.swap(s2); }
  62. istream& operator>>(istream& ci, string& s) {
  63. s.clear();
  64. char ch = ci.get();
  65. while (ch != ' ' && ch != 'n') s += ch, ch = ci.get();
  66. return ci;
  67. }
  68. ostream& operator<<(ostream& co, string& s) {
  69. for (size_t i = 0; i < s.size(); i++) co << s[i];
  70. return co;
  71. }
  72. }
  1. //test.cpp
  2. #include "string.h"
  3. namespace mystr {
  4. void test1() {
  5. string s1 = "1111";
  6. string s2 = s1;
  7. cout << s1.c_str() << endl << s2.c_str() << endl;
  8. cout << s1.size() << endl;
  9. }
  10. void test2() {
  11. string s1 = "111";
  12. string s2 = "222222";
  13. s1 = s2;
  14. cout << s1.c_str() << endl;
  15. }
  16. void test3() {
  17. string s1 = "111222333";
  18. for (auto& i : s1) i += 3;
  19. cout << s1.c_str() << endl;
  20. const string s2 = "111222333";
  21. for (auto& i : s2) cout << i;
  22. cout << endl;
  23. for (size_t i = 0; i < s1.size(); i++) cout << (s1[i] += 2);
  24. cout << endl;
  25. }
  26. void test4() {
  27. string s1 = "sadfsf";
  28. s1.insert(2, '-');
  29. cout << s1.c_str() << endl;
  30. s1.insert(0, '-');
  31. cout << s1.c_str() << endl;
  32. s1.insert(2, "11111");
  33. cout << s1.c_str() << endl;
  34. s1.insert(0, "222222");
  35. cout << s1.c_str() << endl;
  36. }
  37. void test5() {
  38. string s1 = "asgfidsgf";
  39. s1.push_back('-');
  40. cout << s1.c_str() << endl;
  41. s1.append("=====");
  42. cout << s1.c_str() << endl;
  43. s1 += 'w';
  44. cout << s1.c_str() << endl;
  45. s1 += "0000";
  46. cout << s1.c_str() << endl;
  47. s1.erase(10);
  48. cout << s1.c_str() << endl;
  49. s1.erase(7, 100);
  50. cout << s1.c_str() << endl;
  51. s1.erase(3, 2);
  52. cout << s1.c_str() << endl;
  53. s1.erase(0);
  54. cout << s1.c_str() << endl;
  55. }
  56. void test6() {
  57. string s1 = "ksjfghks";
  58. cout << s1.find('h', 2) << endl;
  59. cout << s1.find("ghk", 2) << endl;
  60. cout << s1.find("ghksgs", 2) << endl;
  61. }
  62. void test7(){
  63. string s1 = "sggsdsdf";
  64. string s2 = "sdgfrgdb";
  65. cout << s1.c_str() << endl;
  66. cout << s2.c_str() << endl;
  67. swap(s1, s2);
  68. cout << s1.c_str() << endl;
  69. cout << s2.c_str() << endl;
  70. s1.swap(s2);
  71. cout << s1.c_str() << endl;
  72. cout << s2.c_str() << endl;
  73. string s3 = s1.substr(2, 5);
  74. cout << s3.c_str() << endl;
  75. }
  76. void test8() {
  77. string s1, s2;
  78. cin >> s1 >> s2;
  79. cout << s1 << endl << s2 << endl;
  80. }
  81. }
  82. int main() {
  83. mystr::test8();
  84. return 0;
  85. }