Berbagi teknologi

C Rahasia yang tidak Anda ketahui di STL Heart Resolve (string)

2024-07-12

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

Daftar isi

1. Mengapa mempelajari kelas string?

1.1 String dalam bahasa C

2. Kelas string di perpustakaan standar

2.1 kelas string

2.2 Deskripsi antarmuka umum kelas string

1. Konstruksi umum objek kelas string

2. Operasi pada objek kelas string

3. Deskripsi struktur string pada vs dan g++

3. Implementasi simulasi kelas string

3.2 Salinan dangkal

3.3 Salinan mendalam

3.4 Salin-saat-tulis

3.5 Implementasi simulasi kelas string


1. Mengapa belajarrangkaianbaik?

1,1 detikstring dalam bahasa

C Dalam bahasanya, string dimulai dengan '0' Kumpulan karakter di bagian akhir, untuk kemudahan pengoperasian, C Perpustakaan standar menyediakan beberapa kuat Serangkaian fungsi perpustakaan, tetapi fungsi perpustakaan ini dipisahkan dari string dan tidak sejalan dengan OOP. berpikir, dan ruang di bawahnya perlu dikelola oleh pengguna. Jika Anda tidak berhati-hati, Anda bahkan dapat mengaksesnya di luar batas.

2. di perpustakaan standarrangkaianbaik

2.1 talibaik

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

  • 1. String adalah kelas yang mewakili urutan karakter
  • 2. Dukungan untuk objek tersebut disediakan oleh kelas string standar, yang antarmukanya mirip dengan wadah karakter standar, tetapi dengan tambahan khusus
  • Karakteristik desain string karakter byte tunggal.
  • 3. talikelas sedang menggunakanarang(yaitu sebagai tipe karakternya, gunakan defaultnyasifat_karakterdan tipe pengalokasi(Informasi lebih lanjut tentang templat
  • informasinya, silakan lihat(string_dasar)
  • 4. talikelas adalahstring_dasarSebuah instance dari kelas templat yang menggunakanaranguntuk membuat contohstring_dasarKelas templat, dan penggunaansifat_karakter
  • Danpengalokasisebagaistring_dasarParameter default dari(Untuk informasi templat lebih lanjut, silakan merujuk ke(string_dasar)
  • 5. Perhatikan bahwa kelas ini menangani byte secara independen dari pengkodean yang digunakan:Jika digunakan untuk menangani karakter multi-byte atau panjang variabel(menyukaiBahasa Indonesia: UTF-8)urutan, ini
  • semua anggota kelas(seperti panjang atau ukuran)dan iteratornya, akan tetap dalam byte(alih-alih karakter yang disandikan sebenarnya)mengoperasikan.
Meringkaskan:
  • 1. taliMerupakan kelas string yang mewakili sebuah string
  • 2. Antarmuka kelas ini pada dasarnya sama dengan container biasa, dengan beberapa tambahan yang khusus digunakan untuk mengoperasikannyarangkaianoperasi rutin.
  • 3. taliDi bawah tenda sebenarnya terlihat seperti:string_dasarAlias ​​untuk kelas template,ketik string_dasar<char, char_traits, allocator>
  • rangkaian;
  • 4. Urutan karakter multi-byte atau panjang variabel tidak dapat dioperasikan.
adamenggunakanrangkaiankelas, itu harus berisi#termasukfile header sertamenggunakan namespace std;

2.2 taliDeskripsi antarmuka umum kelas

1. rangkaianKonstruksi umum objek kelas

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

2. rangkaianOperasi objek kelas

Catatan:

  • 1. ukuran() danpanjang()Prinsip penerapan yang mendasari metode ini sama persis, yaitu memperkenalkanukuran()Alasannya adalah agar konsisten dengan antarmuka container lain. Umumnya, size() digunakan.
  • 2. clear() baru saja mengonversirangkaianKarakter valid di dalamnya dihapus dan ukuran ruang di bawahnya tidak diubah.
  • 3. mengubah ukuran(ukuran_t n) dan ubah ukuran(ukuran_t n, char c)Semuanya mengubah jumlah karakter valid dalam string menjadiNPerbedaannya adalah ketika jumlah karakter bertambah: resize(n)menggunakan0untuk mengisi ruang elemen tambahan,ubah ukuran(ukuran_t n, char c)dengan karakterC untuk mengisi ruang elemen tambahan. Catatan: ubah ukuranKetika jumlah elemen diubah, jika jumlah elemen bertambah, ukuran kapasitas di bawahnya dapat diubah. Jika jumlah elemen dikurangi, ukuran total ruang di bawahnya tetap tidak berubah.
  • 4. cadangan(ukuran_t res_arg=0):untukrangkaianCadangan ruang dan jangan mengubah jumlah elemen yang validmenyimpanparameter kurang dari
  • rangkaianKetika ukuran total ruang di bawahnya adalahpencadanganUkuran kapasitas tidak akan diubah.

3. lawanDanBahasa Indonesia: Bahasa Indonesia: g++TurunrangkaianDeskripsi struktur

  Struktur berikut ada di 32 Verifikasi di bawah platform, 32 Penunjuk di bawah platform bit menempati 4 byte.
vsdown Struktur string
string menyumbang total 28 byte , struktur internalnya sedikit lebih rumit dulu Ada serikat pekerja, yang digunakan untuk mendefinisikan Ruang penyimpanan string dalam string:
  1. Jika panjang string kurang dari 16, array karakter tetap internal digunakan untuk menyimpannya.
  2. Ketika panjang string lebih besar dari atau sama dengan16Kapan, ruang terbuka dari tumpukan 
  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;
Desain ini juga masuk akal 16 ,Itu rangkaian Setelah objek dibuat, sudah ada 16 Ruang tetap untuk array karakter, tidak perlu membuatnya melalui heap, dan efisiensi tinggi.
Kedua: Dan satu ukuran_t Bidang tersebut menampung panjang senar, a ukuran_t Bidang menyimpan total kapasitas ruang yang dialokasikan di heap.
Akhirnya: Tetap saja Ada penunjuk Lakukan sesuatu yang lain.
Oleh karena itu, totalnya16+4+4+4=28byte.
Bahasa Indonesia: Bahasa Indonesia: g++ Turun rangkaian Struktur
Bahasa Inggris Turun, rangkaian Ini diimplementasikan melalui copy-on-write. rangkaian Jumlah total objek 4 Bytes, ini hanya berisi sebuah pointer, yang akan menunjuk ke sepotong ruang heap di masa depan, dan berisi bidang-bidang berikut:
  1. ukuran ruang keseluruhan
  2. Panjang tali efektif
  3. Penghitungan referensi
  4. Penunjuk ke ruang tumpukan yang digunakan untuk menyimpan string.
  1. struct _Rep_base
  2. {
  3. size_type _M_length;
  4. size_type _M_capacity;
  5. _Atomic_word _M_refcount;
  6. };

3. taliImplementasi tiruan kelas

PS: Saat mengimplementasikan sendiri kelas string, Anda harus memperhatikan masalah salinan dangkal.

di atas Rangkaian Kelas tidak secara eksplisit mendefinisikan kelebihan konstruktor penyalinan dan operator penugasan. Saat ini, kompiler akan mensintesis yang default bagian 1 struktur membuat bagian 2 , kompiler akan memanggil konstruktor salinan default. Masalah terakhir adalah, bagian 1 bagian 2 Berbagi ruang memori yang sama, blok yang sama ketika dilepaskan Ruang dilepaskan beberapa kali sehingga menyebabkan program mogok , metode penyalinan ini disebut penyalinan dangkal.

 3.2 Salinan dangkal

Salinan dangkal: disebut juga salinan bit, kompiler hanya menyalin nilai dalam objek .jika Kelola sumber daya dalam objek , dan pada akhirnya akan terjadi menghasilkan beberapa objek berjumlah total Berbagi sumber daya yang sama, ketika suatu objek dihancurkan, sumber daya tersebut akan dilepaskan. Pada saat ini, objek lain tidak mengetahui bahwa sumber daya tersebut telah dilepaskan dan mengira bahwa sumber daya tersebut telah dilepaskan. Itu masih valid, jadi ketika Anda terus mengakses sumber daya, akan terjadi pelanggaran akses.

3.3 salinan yang dalam

Jika suatu kelas melibatkan pengelolaan sumber daya, konstruktor salinannya, kelebihan beban operator penugasan, dan destruktornya harus diberikan secara eksplisit.Umumnya, ini disediakan dalam mode penyalinan mendalam.

3.4 salin saat menulis

Copy-on-write merupakan salah satu jenis penundaan yang dilakukan dengan menambahkan penghitungan referensi berdasarkan salinan dangkal.
Penghitungan referensi: digunakan untuk mencatat jumlah pengguna sumber daya.Pada waktu konstruksi, hitungan sumber daya diberikan sebagai 1 , setiap kali objek tambahan menggunakan sumber daya, hitungannya bertambah 1 , ketika suatu objek dihancurkan, kurangi jumlahnya terlebih dahulu 1 , lalu periksa apakah sumber daya perlu dilepaskan, jika dihitung 1 , menunjukkan bahwa objek tersebut adalah pengguna terakhir sumber daya dan melepaskan sumber daya tersebut; jika tidak, maka tidak dapat dilepaskan karena ada objek lain yang menggunakan sumber daya tersebut.

Tali 3,5Implementasi tiruan kelas

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