『 C++入門到放棄 』- string

C++ 學習筆記 - string

一、什麼是string ?

  • string 是 C++ 中標準函數庫中的一個類,其包含在 中

  • 該類封裝了C語言中字符串操作,提供內存管理自動化與更多的操作

  • 支持複製、比較、插入、刪除、查找等功能

二、常用接口整理

類別常用方法 / 說明
建立與指定std::string s = "hello";
長度s.size()s.length()
存取s[0]s.at(0)(有邊界檢查)
新增s.push_back('!')s.append(" world")
插入s.insert(5, "123")
刪除s.erase(3, 2)
擷取s.substr(0, 5)
查找s.find("abc")s.rfind("abc")
清空s.clear()
比較==, !=, <, >, compare()
C 字串轉換s.c_str()

三、與 C-Style 字符串比較

項目C-style (char*)C++ string (std::string)
結尾需手動補上 \0自動管理
長度需用 strlen()size() 即可
記憶體管理自行分配與釋放自動處理
易出錯高(溢位、未終止)低(有邊界檢查)
與 C API 相容原生透過 c_str() 支援

c_str()的使用範例

std::string s = "data.txt";
FILE* fp = fopen(s.c_str(), "r"); // 將 string 轉為 const char*
// c_str() 回傳 const char*,供 C 語言函數(如 fopen, printf, strcpy)使用

*注意事項

  • s[999] → 未檢查邊界會造成未定行為(undefined behavior)
  • c_str() 回傳值不可修改
  • erase()insert() 修改後會讓舊的指標失效(如 iterator 或 c_str()

四、String 類模擬實現

1. 成員變量和 typedef
size_t _size;        // 實際字串長度
size_t _capacity;    // 分配空間大小
char* _str;          // C-style 字串
typedef char* iterator;
typedef const char* const_iterator;
static size_t npos;  // 用來表示無法找到的位置(類似 STL 的 string::npos)
// 靜態變量 類內聲明 類外定義
size_t string::npos = -1;
2. 構造、析構、拷貝
// 構造函數
string(const char *str = "") // 帶參
{_size = strlen(str);_capacity = strlen(str);_str = new char[_capacity + 1];// strcpy(_str, str);memcpy(_str, str, _size + 1);/*
之所以使用memcpy而不是strcpy是因爲strcpy只會複製'/0'之前的的字符 如果字符串是一個沒有'/0'的字符串則會有越界的問題
*/
}// 析構函數
~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}// 拷貝構造函數string(const string& s): _str(new char[strlen(s._str) + 1]) {strcpy(_str, s._str);}
3. 迭代器
// iterator
iterator begin()
{return _str; // 返回首元素的地址
}
iterator end()
{return _str + _size; // 返回首元素的地址 + 有多少個元素
}// const_iterator
const_iterator begin() const
{return _str;
}
const_iterator end() const
{return _str + _size;
}
4. 內存管理
void reserve(size_t n) // 多少有效字符的空間 假設要10個有效字符會開11個空間
{if (n > _capacity){char *tmp = new char[n + 1];// strcpy(tmp, _str);memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}
}
void resize(size_t n, char ch = '\0') 
{if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}
}
5. 增刪查改 ( 函數接口 )
void push_back(char ch)
{if (_capacity == _size){// 2倍擴容reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;++_size;_str[_size] = '\0';
}
// ------------------------------------------------------------------------------------------
void append(const char *str)
{size_t len = strlen(str);if (_size + len > _capacity){// 至少擴容至 _size + lenreserve(_size + len);}// strcpy(_str + _size, str); // strcpy 會把字符串的'\0'一起拷貝過去memcpy(_str + _size, str, len + 1);_size += len;
}
// ------------------------------------------------------------------------------------------
// insert pos位置插入n個字元
void insert(size_t pos, size_t n, char ch)
{assert(pos <= _size);// 至少擴容至 _size + nif (_size + n > _capacity){reserve(_size + n);}// 挪動數據size_t end = _size;               // end or pos 類型為 size_t 時 當 pos = 0 會發生問題while (end >= pos && end != npos) /* 可以嘗試(int) 強轉pos類型 => 要轉是因爲 end 為int pos為size_t 時會發生類型提升(提升至較大的類型 : size_t) 會陷入死循環*/{_str[end + n] = _str[end];--end; // size_t 不會小於 0 所以如果 end 類型為 size_t 會陷入死循環}for (size_t i = 0; i < n; ++i){_str[pos + i] = ch;}_size += n;
}
// ------------------------------------------------------------------------------------------
// insert pos位置插入字符串
void insert(size_t pos, const char *str)
{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}// 挪動數據size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];--end;}for (size_t i = 0; i < len; ++i){_str[pos + i] = str[i];}_size += len;
}
// 刪除
void erase(size_t pos = 0, size_t len = npos)
{assert(pos <= _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;// _str[_size] = '\0';}else{size_t end = pos + len;while (end <= _size){_str[pos++] = _str[end++];}_size -= len;}
}
// ------------------------------------------------------------------------------------------
void clear()
{_str[0] = '\0';_size = 0;
}
// 查
size_t find(char ch, size_t pos = 0) const
{assert(pos < _size);for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;}}return npos;
}
// ------------------------------------------------------------------------------------------
size_t find(const char *str, size_t pos = 0) const
{assert(pos < _size);const char *ptr = strstr(_str + pos, str);if (ptr){return ptr - _str;}else{return npos;}
}
// ------------------------------------------------------------------------------------------
string substr(size_t pos = 0, size_t len = npos) const
{assert(pos < _size);size_t n = len;if (len == npos || pos + len > _size){n = _size - pos;}string tmp;tmp.reserve(n);for (size_t i = pos; i < n; ++i){tmp += _str[i];}return tmp;
}
6. 運算符重載
char &operator[](size_t pos) // 重載運算符 讓string也可以像C char 一樣用下標訪問
{assert(pos < _size);return _str[pos];
}
const char &operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}
// ------------------------------------------------------------------------------------------
string &operator+=(char ch)
{push_back(ch);return *this;
}
string &operator+=(const char *str)
{append(str);return *this;
}// ------------------------------------------------------------------------------------------
string &operator=(string tmp)
{swap(tmp);return *this;
}
// swap函數
void swap(string &str)
{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);
}
// ------------------------------------------------------------------------------------------
bool operator<(const string &str) const
{// return strcmp(this->_str, str._str) < 0;size_t i1 = 0;size_t i2 = 0;while (i1 < _size && i2 < str._size){if (_str[i1] < str._str[i2]){return true;}else if (_str[i1] > str._str[i2]){return false;}else{i1++;i2++;}}if (i1 == _size && i2 != str._size){return true;}else{return false;}
}
// ------------------------------------------------------------------------------------------
bool operator==(const string &s) const
{return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
// ------------------------------------------------------------------------------------------
bool operator<=(const string &s) const
{return *this < s || *this == s;
}
// ------------------------------------------------------------------------------------------
bool operator>(const string &s) const
{return !(*this <= s);
}
// ------------------------------------------------------------------------------------------
bool operator>=(const string &s) const
{return !(*this < s);
}
// ------------------------------------------------------------------------------------------
bool operator!=(const string &s) const
{return !(*this == s);
}
ostream &operator<<(ostream &out, const bit::string &s)
{for (auto ch : s){out << ch;}return out;
}
// ------------------------------------------------------------------------------------------
istream &operator>>(istream &in, bit::string &s)
{char ch = in.get();char buf[128];int i = 0;// 處理緩衝區之前的空格或換行while (ch == ' ' || ch == '\0'){ch = in.get();}while (ch != ' ' && ch != '\0'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}}if (i != 0){buf[i] = '\0';s += buf;}return in;
}
}
7. 其他
char *c_str() const // C的string
{return _str;
}
size_t size() //元素個數
{return _size;
}

五、完整代碼

#pragma once
#include <iostream>
#include <string>
#include <assert.h>
// 因爲library中已有string 所以以命名空間做區分
using namespace std;
namespace bit
{class string{public:typedef char *iterator;typedef const char *const_iterator;// iteratoriterator begin(){return _str; // 返回首元素的地址}iterator end(){return _str + _size; // 返回首元素的地址 + 有多少個元素}// const_iteratorconst_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}string(const char *str = "") // 帶參{_size = strlen(str);_capacity = strlen(str);_str = new char[_capacity + 1];memcpy(_str, str, _size + 1);}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}char *c_str() const // C的string{return _str;}size_t size(){return _size;}char &operator[](size_t pos) // 重載運算符 讓string也可以像C char 一樣用下標訪問{assert(pos < _size);return _str[pos];}const char &operator[](size_t pos) const{assert(pos < _size);return _str[pos];}void reserve(size_t n) // 多少有效字符的空間 假設要10個有效字符會開11個空間{if (n > _capacity){char *tmp = new char[n + 1];// strcpy(tmp, _str);memcpy(tmp, _str, _size + 1);delete[] _str;_str = tmp;_capacity = n;}}void resize(size_t n, char ch = '\0'){if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}}// 增刪查改// 增void push_back(char ch){if (_capacity == _size){// 2倍擴容reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}void append(const char *str){size_t len = strlen(str);if (_size + len > _capacity){// 至少擴容至 _size + lenreserve(_size + len);}// strcpy(_str + _size, str); // strcpy 會把字符串的'\0'一起拷貝過去memcpy(_str + _size, str, len + 1);_size += len;}string &operator+=(char ch){push_back(ch);return *this;}string &operator+=(const char *str){append(str);return *this;}void insert(size_t pos, size_t n, char ch){assert(pos <= _size);// 至少擴容至 _size + nif (_size + n > _capacity){reserve(_size + n);}// 挪動數據size_t end = _size;             while (end >= pos && end != npos) {_str[end + n] = _str[end];--end; // size_t 不會小於 0 所以如果 end 類型為 size_t 會陷入死循環}for (size_t i = 0; i < n; ++i){_str[pos + i] = ch;}_size += n;}void insert(size_t pos, const char *str){assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}// 挪動數據size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];--end;}for (size_t i = 0; i < len; ++i){_str[pos + i] = str[i];}_size += len;}// 刪除void erase(size_t pos = 0, size_t len = npos){assert(pos <= _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;// _str[_size] = '\0';}else{size_t end = pos + len;while (end <= _size){_str[pos++] = _str[end++];}_size -= len;}}size_t find(char ch, size_t pos = 0) const{assert(pos < _size);for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;}}return npos;}size_t find(const char *str, size_t pos = 0) const{assert(pos < _size);const char *ptr = strstr(_str + pos, str);if (ptr){return ptr - _str;}else{return npos;}}string substr(size_t pos = 0, size_t len = npos) const{assert(pos < _size);size_t n = len;if (len == npos || pos + len > _size){n = _size - pos;}string tmp;tmp.reserve(n);for (size_t i = pos; i < n; ++i){tmp += _str[i];}return tmp;}void clear(){_str[0] = '\0';_size = 0;}void swap(string &str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}string &operator=(string tmp){swap(tmp);return *this;}bool operator<(const string &str) const{// return strcmp(this->_str, str._str) < 0;size_t i1 = 0;size_t i2 = 0;while (i1 < _size && i2 < str._size){if (_str[i1] < str._str[i2]){return true;}else if (_str[i1] > str._str[i2]){return false;}else{i1++;i2++;}}if (i1 == _size && i2 != str._size){return true;}else{return false;}}bool operator==(const string &s) const{return _size == s._size && memcmp(_str, s._str, _size) == 0;}bool operator<=(const string &s) const{return *this < s || *this == s;}bool operator>(const string &s) const{return !(*this <= s);}bool operator>=(const string &s) const{return !(*this < s);}bool operator!=(const string &s) const{return !(*this == s);}private:size_t _size;size_t _capacity;char *_str;static size_t npos; // 第二種insert pos = 0 死循環解決方法 : 設定 npos};// 靜態變量 類內聲明 類外定義size_t string::npos = -1;ostream &operator<<(ostream &out, const bit::string &s){for (auto ch : s){out << ch;}return out;}istream &operator>>(istream &in, bit::string &s){char ch = in.get();// in >> ch;char buf[128];int i = 0;// 處理緩衝區之前的空格或換行while (ch == ' ' || ch == '\0'){ch = in.get();}while (ch != ' ' && ch != '\0'){buf[i++] = ch;if (i == 127){buf[i] = '\0';s += buf;i = 0;}}if (i != 0){buf[i] = '\0';s += buf;}return in;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87678.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87678.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87678.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ARM架構下C++程序堆溢出與棧堆碰撞問題深度解析

ARM架構下C程序堆溢出與棧堆碰撞問題深度解析 一、問題背景&#xff1a;從崩潰現象到內存異常 在嵌入式系統開發中&#xff0c;程序崩潰是常見但棘手的問題。特別是在ARM架構設備上&#xff0c;一種典型的崩潰場景如下&#xff1a;程序在執行聚類算法或大規模數據處理時突然終…

.NET9 實現排序算法(MergeSortTest 和 QuickSortTest)性能測試

在 .NET 9 平臺下&#xff0c;我們對兩種經典的排序算法 MergeSortTest&#xff08;歸并排序&#xff09;和 QuickSortTest&#xff08;快速排序&#xff09;進行了性能基準測試&#xff08;Benchmark&#xff09;&#xff0c;以評估它們在不同數據規模下的執行效率、內存分配及…

RabbitMQ - SpringAMQP及Work模型

一、概述RabbitMQ是一個流行的開源消息代理&#xff0c;支持多種消息傳遞協議。它通常用于實現異步通信、解耦系統組件和分布式任務處理。Spring AMQP是Spring框架下的一個子項目&#xff0c;提供了對RabbitMQ的便捷訪問和操作。本文將詳細介紹RabbitMQ的工作模型&#xff08;W…

微信小程序51~60

1.界面交互-loading提示框 loading提示框用于增加用戶體驗&#xff0c; 對應的API有兩個&#xff1a; wx.showLoading()顯示loading提示框wx.hideLoading()關閉loading提示框 Page({getData () {//顯示loading提示框wx.showLoading({//提示內容不會自動換行&#xff0c;多出來的…

SqueezeBERT:計算機視覺能為自然語言處理在高效神經網絡方面帶來哪些啟示?

摘要 人類每天閱讀和撰寫數千億條消息。得益于大規模數據集、高性能計算系統和更優的神經網絡模型&#xff0c;自然語言處理&#xff08;NLP&#xff09;技術在理解、校對和組織這些消息方面取得了顯著進展。因此&#xff0c;將 NLP 部署于各類應用中&#xff0c;以幫助網頁用…

Springboot開發常見注解一覽

注解用法常用參數Configuration用于標記類為配置類&#xff0c;其中通過Bean方法定義Spring管理的組件。它替代XML配置&#xff0c;用Java代碼聲明對象創建邏輯&#xff0c;并確保單例等容器特性生效。相當于給Spring提供一個“制造說明書”來組裝應用部件RestControllerRestCo…

Maven高級——分模塊設計與開發

目錄 ?編輯 分模塊設計與開發 拆分策略 繼承與聚合 版本鎖定 聚合 作用 實現 Maven中繼承與聚合的聯系與區別&#xff1f; 聯系 區別 私服 分模塊設計與開發 將一個大項目拆分成若干個子模塊&#xff0c;方便項目的管理維護&#xff0c;擴展&#xff0c;也方便模…

線程池的七個參數設計源于對高并發場景下資源管理、系統穩定性與性能平衡的深刻洞察

?? 一、核心參數設計目標與解決的問題 參數設計目標解決的核心問題典型取值策略corePoolSize&#xff08;核心線程數&#xff09;維持常備線程資源避免頻繁創建/銷毀線程的開銷&#xff0c;提高響應速度CPU密集型&#xff1a;N_cpu 1 IO密集型&#xff1a;2 N_cpu maximum…

少樣本學習在計算機視覺中的應用:原理、挑戰與最新突破

在深度學習的黃金時代&#xff0c;大量標注數據似乎成了算法性能的前提。然而在許多現實場景中&#xff0c;如醫療圖像分析、工業缺陷檢測、遙感識別、甚至個性化視覺服務中&#xff0c;高質量、成規模的標注數據往往昂貴、稀缺&#xff0c;甚至難以獲得。這種場景正是**少樣本…

github在線圖床

github做的圖床&#xff0c;原理是利用github API實現的在線上傳&#xff0c;就一個頁面&#xff0c;css和js都是集成在頁面&#xff0c;相關信息保存在瀏覽器緩存中&#xff0c;配置一下即可使用 效果演示&#xff1a; github在線圖床 打開網站填寫下列信息 github用戶名&a…

css-多條記錄,自動換行與自動并行布局及gap兼容

實現這樣的內容布局&#xff0c;當一段文案長度超過當前行的時候自動占據一行&#xff0c;其他相近的不超過一行自動放在一行間隔隔開 關鍵實現原理&#xff1a; 彈性布局容器&#xff1a; .history-container {display: flex;flex-wrap: wrap;gap: 12px; }使用flex-wrap: wr…

Redis 哨兵模式部署--docker版本

redis sentinel 簡介 Redis Sentinel 是 Redis 官方提供的高可用&#xff08;HA&#xff09;解決方案&#xff0c;用于監控主從架構中的故障并自動完成故障轉移。當主節點&#xff08;Master&#xff09;宕機時&#xff0c;Sentinel 能自動選舉新的主節點&#xff0c;通知從節…

Java線程中的守護線程

Java線程中的守護線程在Java中&#xff0c;守護線程&#xff08;Daemon Thread&#xff09;是一種特殊類型的線程&#xff0c;它在后臺運行&#xff0c;主要用于支持其他線程&#xff08;如用戶線程&#xff09;的工作。守護線程不會阻止JVM&#xff08;Java虛擬機&#xff09;…

Flink-狀態恢復-isRestore分析

isRestored 方法返回值依賴 restoredCheckpointId 是否為空&#xff1a;restoredCheckpointId 在算子狀態句柄&#xff08;StreamOperatorStateHandler&#xff09;中從 StreamOperatorStateContext 獲取并賦值給 StateInitializationContext&#xff08;該 context 就是 initi…

rk3128 emmc顯示剩余容量為0

機器emmc 容量顯示異常&#xff0c;顯示剩余容量為0&#xff0c;這時候做了一個讓 系統不檢測GPP分區部分的操作&#xff0c;此問題才得以解決&#xff0c;如下&#xff1a; system/vold/DirectVolume.cpp -33,6 33,8 #include "VolumeManager.h"#include "Re…

WebAssembly國際化多語種支持

icu linux數據裁剪 先linux編譯出所有的工具 mkdir build && cd build ../configure --prefix=$(pwd)/build_wasm/install --enable-static --disable-shared --with-data-packaging=static --enable-tools=yes --enable-extras=yes --e…

Ubuntu 安裝 etcd 與 etcd-cpp-apiv3

目錄 安裝 etcd 安裝 etcd-cpp-apiv3 安裝 etcd sudo apt update sudo apt install etcd-server sudo apt install -y etcd-client 在 /etc/default/etcd 配置文件中配置&#xff0c;下面示例是單個服務器內進程之間交換信息且只有一個etcd節點。 #節點名稱&#xff0c;默認為…

Spring Boot 集成 GeoTools 詳解

目錄 一、概述二、集成優勢三、集成步驟四、使用場景五、案例&#xff1a;周邊設施查詢系統六、注意事項七、總結 一、概述 什么是 Spring Boot&#xff1f; Spring Boot 是由 Pivotal 團隊開發的基于 Spring 框架的快速開發工具&#xff0c;它通過自動配置、起步依賴等特性簡…

基礎知識:mysql-connector-j依賴

mysql-connector-j 是 MySQL 官方提供的 Java 數據庫連接驅動&#xff08;JDBC Driver&#xff09;&#xff0c;用于在 Java 應用程序中連接和操作 MySQL 數據庫。它是 MySQL 8.0 版本之后的標準驅動名稱&#xff0c;替代了舊的 mysql-connector-java。 一、新舊版本對比 驅動…

vscode remote-ssh 拓展免密訪問 linux虛擬機

前置步驟&#xff0c;在linux安裝好ssh并且win可以使用密碼登錄linux sudo apt install openssh-server -y 在win上檢查密鑰是否存在 檢查公鑰和私鑰cat ~/.ssh/id_rsa.pubcat ~/.ssh/id_rsa 如果不存在&#xff0c;重新生成 ssh-keygen -t rsa -b 4096 重新執行 cat ~/.ssh/…