【C++進階二】string的模擬實現

【C++進階二】string的模擬實現

  • 1.構造函數和C_str
    • C_str:
  • 2.operator[]
  • 3.拷貝構造
    • 3.1淺拷貝
    • 3.2深拷貝
  • 4.賦值
  • 5.迭代器
  • 6.比較ascll碼值的大小
  • 7.reverse擴容
  • 8.push_back尾插和append尾插
  • 9.+=
  • 10.insert
    • 10.1在pos位置前插入字符ch
    • 10.2在pos位置前插入字符串str
  • 11.resize
  • 12.erase
    • 12.1從pos位置開始刪除len個數據
    • 12.2當pos+len<總長度時,使用strcpy函數拷貝,從而覆蓋刪除要被刪除的字符
    • 12.3當pos+len大于總長度或者len等于npos時,剩余長度全部刪除
  • 13.流插入<<
  • 14.流提取 >>

string底層是一個字符數組,為了跟庫里的string區別,我們定義了一個命名空間將string類包含咋內

1.構造函數和C_str

錯誤示范:

在這里插入圖片描述
使用new開辟空間可以再次改進構造函數:
在這里插入圖片描述
最終優化為全缺省的構造函數:
在這里插入圖片描述

不可以將缺省值設置成nullptr,strlen(str)對于str指針解引用,遇到’\0’終止,解引用NULL會報錯。將缺省值設置成一個空字符串,結尾默認為’\0’

C_str:


const char* C_str()//返回const char*類型的指針 
{return  _str;
}

返回const char*類型的指針相當于返回字符串

2.operator[]

由于可能存在 string與const string類型,所以設置成兩個函數構成函數重載

char& operator[](size_t pos)//operator[]
{return _str[pos];
}
char& operator[](size_t pos)const//operator[]的函數重載
{return _str[pos];
}

3.拷貝構造

3.1淺拷貝

拷貝構造函數如果不寫編譯器會自動生成,對于內置類型完成值拷貝或者淺拷貝,若使用編譯器自動生成的拷貝構造就會報錯

string s2("hello world");
string s3(s2);

在這里插入圖片描述

3.2深拷貝

string(const string& s)//拷貝構造:_size(s._size),_capaicty(s._capaicty)
{_str = new char[_capaicty + 1];//開辟一塊空間strcpy(_str, s._str);//將s2拷貝給s1
}

創建一塊同樣大小的空間,并將原來的數據拷貝下來,這樣就是s2與s3指向各自的空間,一個被修改也不會影響另一個
在這里插入圖片描述

4.賦值

賦值運算符也是默認成員函數,如果不寫會進行淺拷貝/值拷貝
在這里插入圖片描述

  1. 第一種兩者空間大小相同,進行值拷貝
  2. 第二種s1的空間遠大于s2的空間,進行值拷貝會浪費空間,所以系統會按照第三種做法執行
  3. 第三種,s1的空間太小,需要new開辟一塊空間,將舊空間銷毀,將s2拷貝到新開辟的空間

編譯器默認不會這樣處理,它會直接將舊空間釋放,再去開新空間,并將值拷貝過來

string& operator=(const string& s)//賦值 s1=s3
{if(this != &s)//排除賦值本身的情況{char* tmp = new char[s._capaicty + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capaicty = s._capaicty;}return *this;
}

若釋放舊空間,如果new失敗了,則破壞原有空間,所以使用一個臨時變量tmp接收開辟的空間,如果new成功將tmp傳給_str,若new失敗也不會破壞s1空間

5.迭代器

使用typedef 分別將用iterator代替 char*,const_iterator代替 const char*

typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}
   
iterator begin()const //指針指向的內容不能被修改
{return _str;
}
iterator end()const
{return _str + _size;
}

在這里插入圖片描述

6.比較ascll碼值的大小

通過C語言函數strcmp,比較字符串從頭開始字符的ASCII值,再通過復用來實現剩下的
如果不小心在復用時將const修飾的傳給非const成員就會報錯,所以括號外面加上const,修飾this指針

bool operator==(const string& s)const //s1==s2
{return strcmp(_str, s._str)==0;
}
bool operator<(const string& s)const //s1<s2
{return strcmp(_str, s._str) < 0;
}
bool operator<=(const string& s)const //s1<=s2
{return *this < s || *this == s;
}
bool operator>(const string& s)const //s1>s2
{return !(*this <= s);
}
bool operator>=(const string& s)const //s1>=s2
{//復用return *this > s || *this == s;
}
bool operator!=(const string& s)const //s1!=s2
{return !(*this == s);
}

7.reverse擴容

void reserve(size_t n)//開辟空間
{if(n > _capacity)//防止縮容的問題{char* tmp = new char[n + 1];//多開一個'\0'strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;//計算有效}
}

在這里插入圖片描述
為了防止new失敗,所以使用臨時變量tmp指向new出來的空間,若new成功,釋放舊空間,并將_str指向新空間

8.push_back尾插和append尾插

通過reserve進行類似擴容的操作,再將ch賦值給當前最后一個字符

void push_back(char ch)//尾插字符
{if(_size + 1 > _capaicty){reserve(2 * _capaicty);//開辟2倍空間}_str[_size] = ch;_size++;
}
void append(const char* str)//尾插 字符串
{ int len = strlen(str);if(_size + len > _capaicty){reserve(_size + len);}strcpy(_str + _size, str);//在原來的字符串后拷貝字符串_size += len;
}

9.+=

string& operator+=(char ch)//+= 字符
{push_back(ch);return *this;
}string& operator+=(const char* str)//+= 字符串 函數重載
{append(str);return *this;
}

10.insert

10.1在pos位置前插入字符ch

void insert(size_t pos, char ch)//在pos位置前插入字符ch
{if(_size + 1 > _capacity)//擴容{reserve(2 * _capacity);}size_t end = _size + 1;//'\0'的下一個位置while(end > pos){//把前面傳給后面_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;
}

由于pos與end都是size_t類型,沒有負數,所以當while循環條件設置為end>=pos時,如果pos=0,end–-,end變為負數,計算的就是其補碼了,所以while(end>=pos)一直成立,無法結束循環

在這里插入圖片描述
把前面的字符傳給后面的字符,當end下標為1時,end-1的下標為0,循環結束

10.2在pos位置前插入字符串str

void insert(size_t pos, const char* str)//在pos位置前插入字符串str
{int len = strlen(str);if(_size + len > _capacity)//擴容{reserve(_size + len);}size_t end = _size + len;while(end > pos+len-1){//把前面傳給后面_str[end] = _str[end-len];end--;}strncpy(_str + pos, str, len);//拷貝len個字節,不包含'\0'_size += len;
}

在這里插入圖片描述

為了保證最后一次循環時,下標end-len在下標為0的位置上,所以取end為pos+len,如果end>pos+len-1不成立時終止循環,最后一次end取值即為pos+len
使用strncpy函數,將不包含’\0’的str拷貝到_str+pos下標位置

11.resize

分為三種情況

  1. n<size 刪除數據
  2. size<n<capacity 剩余空間初始化
  3. n>capacity 擴容+初始化
void resize(size_t n,char ch)//開辟空間+初始化
{if(n <= _size)//刪除數據保留前n個{_size = n;_str[n] = '\0';}else//n>_size{if(n >_capacity)//擴容{reserve(n);}int i = _size;while(i < n)//剩余空間初始化為ch{_str[i] = ch;i++;}_size = n;_str[_size] = '\0';}
}

12.erase

12.1從pos位置開始刪除len個數據

static const size_t npos = -1;
string& erase(size_t pos = 0, size_t len = npos)//從pos位置開始刪除len個數據
{if(len==npos||pos+len>=_size){//全部刪除_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str +pos+len);//包含'\0'_size -= len;}return *this;
}

12.2當pos+len<總長度時,使用strcpy函數拷貝,從而覆蓋刪除要被刪除的字符

12.3當pos+len大于總長度或者len等于npos時,剩余長度全部刪除

13.流插入<<

使用友元函數是為了在類外面調用類的私有的成員變量,若不需要調用則不用友元函數

ostream& operator<<(ostream& out, const string&s)
{int i = 0;for(i = 0; i < s.size(); i++){out << s[i] << " ";}return out;
}

實現流插入不可以調用C_str(),因為C_str()返回的是一個字符串,遇見’\0’就會結束,但若打印結果有好幾個’\0’,則遇見第一個就會結束,不符合預期

14.流提取 >>

輸入多個值,C++規定空格換行是值與值之間的區分
錯誤示范:

istream& operator>>(istream& in,  string& s)//>>
{char ch;in >> ch;while(ch != ' ' && ch != '\n'){s += ch;in >> ch;}return in;
}
  1. 上述代碼在循環中無法找到空格/換行,導致循環無法停止
  2. 輸入的數據存放在緩沖區中,使用循環在緩沖區中提取數據,但是空格/換行不在緩沖區中,因為認為它是多個值之間的間隔
  3. 使用get就不會認為空格/換行是多個值之間的間隔,若遇見空格/換行就會存放緩沖區中等待提取
istream& operator>>(istream& in,  string& s)//>>
{s.clear();char ch = in.get();char buf[128];size_t index = 0;while(ch != ' ' && ch != '\n'){buf[index++] = ch;if(index == 127)//為了防止頻繁擴容{buf[127] = '\0';s += buf;index = 0;}ch = in.get();}if(index != 0){buf[index] = '\0';s += buf;}return in;
}

當需要輸入的string對象中有值存在時,需要先使用clear清空,再輸入新的數據
為了避免頻繁擴容,使用一個128的字符數組接收,若輸入的數據比128小,跳出循環將數組中的數據傳給string類s,若輸入的數據比128大,則將字符數組整體傳給string類s,再正常擴容

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

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

相關文章

wokwi arduino mega 2560 - 點亮LED案例

截圖&#xff1a; 點亮LED案例仿真截圖 代碼&#xff1a; unsigned long t[20]; // 定義一個數組t&#xff0c;用于存儲20個LED的上次狀態切換時間&#xff08;單位&#xff1a;毫秒&#xff09;void setup() {pinMode(13, OUTPUT); // 將引腳13設置為輸出模式&#xff08;此…

vue3項目使用 python +flask 打包成桌面應用

server.py import os import sys from flask import Flask, send_from_directory# 獲取靜態文件路徑 if getattr(sys, "frozen", False):# 如果是打包后的可執行文件base_dir sys._MEIPASS else:# 如果是開發環境base_dir os.path.dirname(os.path.abspath(__file…

后端學習day1-Spring(八股)--還剩9個沒看

一、Spring 1.請你說說Spring的核心是什么 參考答案 Spring框架包含眾多模塊&#xff0c;如Core、Testing、Data Access、Web Servlet等&#xff0c;其中Core是整個Spring框架的核心模塊。Core模塊提供了IoC容器、AOP功能、數據綁定、類型轉換等一系列的基礎功能&#xff0c;…

LeetCode 第34、35題

LeetCode 第34題&#xff1a;在排序數組中查找元素的第一個和最后一個位置 題目描述 給你一個按照非遞減順序排列的整數數組nums&#xff0c;和一個目標值target。請你找出給定目標值在數組中的開始位置和結束位置。如果數組中不存在目標值target&#xff0c;返回[-1,1]。你必須…

告別分庫分表,時序數據庫 TDengine 解鎖燃氣監控新可能

達成效果&#xff1a; 從 MySQL 遷移至 TDengine 后&#xff0c;設備數據自動分片&#xff0c;運維更簡單。 列式存儲可減少 50% 的存儲占用&#xff0c;單服務器即可支撐全量業務。 毫秒級漏氣報警響應時間控制在 500ms 以內&#xff0c;提升應急管理效率。 新架構支持未來…

第十四屆藍橋杯真題

一.LED 先配置LED的八個引腳為GPIO_OutPut,鎖存器PD2也是,然后都設置為起始高電平,生成代碼時還要去解決引腳沖突問題 二.按鍵 按鍵配置,由原理圖按鍵所對引腳要GPIO_Input 生成代碼,在文件夾中添加code文件夾,code中添加fun.c、fun.h、headfile.h文件,去資源包中把lc…

《基于機器學習發電數據電量預測》開題報告

個人主頁&#xff1a;大數據蟒行探索者 目錄 一、選題背景、研究意義及文獻綜述 &#xff08;一&#xff09;選題背景 &#xff08;二&#xff09;選題意義 &#xff08;三&#xff09;文獻綜述 1. 國內外研究現狀 2. 未來方向展望 二、研究的基本內容&#xff0c;擬解…

UWP程序用多頁面實現應用實例多開

Windows 10 IoT ARM64平臺下&#xff0c;UWP應用和MFC程序不一樣&#xff0c;同時只能打開一個應用實例。以串口程序為例&#xff0c;如果用戶希望同時打開多個應用實例&#xff0c;一個應用實例打開串口1&#xff0c;一個應用實例打開串口2&#xff0c;那么我們可以加載多個頁…

Springboot整合Netty簡單實現1對1聊天(vx小程序服務端)

本文功能實現較為簡陋&#xff0c;demo內容僅供參考&#xff0c;有不足之處還請指正。 背景 一個小項目&#xff0c;用于微信小程序的服務端&#xff0c;需要實現小程序端可以和他人1對1聊天 實現功能 Websocket、心跳檢測、消息持久化、離線消息存儲 Netty配置類 /*** au…

GitLab 中文版17.10正式發布,27項重點功能解讀【二】

GitLab 是一個全球知名的一體化 DevOps 平臺&#xff0c;很多人都通過私有化部署 GitLab 來進行源代碼托管。極狐GitLab 是 GitLab 在中國的發行版&#xff0c;專門為中國程序員服務。可以一鍵式部署極狐GitLab。 學習極狐GitLab 的相關資料&#xff1a; 極狐GitLab 官網極狐…

好消息!軟航文檔控件(NTKO WebOffice)在Chrome 133版本上提示擴展已停用的解決方案

軟航文檔控件現有版本依賴Manifest V2擴展技術支持才能正常運行&#xff0c;然而這個擴展技術到2025年6月在Chrome高版本上就徹底不支持了&#xff0c;現在Chrome 133開始的版本已經開始彈出警告&#xff0c;必須手工開啟擴展支持才能正常運行。那么如何解決這個技術難題呢&…

字典樹與01trie

字典樹簡介 當我們通過字典查一個字或單詞的時候&#xff0c;我們會通過前綴或關鍵字的來快速定位一個字的位置&#xff0c;進行快速查找。 字典樹就是類似字典中索引表的一種數據結構&#xff0c;能夠幫助我們快速定位一個字符串的位置。 字典樹是一種存儲字符串的數據結構…

二十五、實戰開發 uni-app x 項目(仿京東)- 前后端輪播圖

定義了一個名為 Swiper 的Java類,用于表示一個輪播圖實體。它使用了 Jakarta Persistence API (JPA) 來映射數據庫表,并使用了 Lombok 庫來簡化代碼。以下是對代碼的詳細講解: 1. 包聲明 package com.jd.jdmall.model; 這行代碼聲明了該類所在的包路徑為 com.jd.jdmall.mode…

游戲搖桿開發:利用 Windows API 實現搖桿輸入捕獲

在現代游戲開發中&#xff0c;游戲搖桿&#xff08;Joystick&#xff09;作為一種重要的輸入設備&#xff0c;能夠為玩家提供更加沉浸式的游戲體驗。Windows 操作系統提供了一系列 API 函數&#xff0c;允許開發者輕松地捕獲和處理游戲搖桿的輸入。本文將介紹如何使用 Windows …

Ceph集群2025(Squid版)快速對接K8S cephFS文件存儲

ceph的塊存儲太簡單了。所以不做演示 查看集群 創建一個 CephFS 文件系統 # ceph fs volume create cephfs01 需要創建一個子卷# ceph fs subvolume create cephfs01 my-subvol -----------------#以下全部自動創建好 # ceph fs ls name: cephfs01, metadata pool: c…

Python中數據結構元組詳解

在Python中&#xff0c;元組&#xff08;Tuple&#xff09;是一種不可變的序列類型&#xff0c;常用于存儲一組有序的數據。與列表&#xff08;List&#xff09;不同&#xff0c;元組一旦創建&#xff0c;其內容無法修改。本文將詳細介紹元組的基本操作、常見運算、內置函數以及…

游戲引擎學習第183天

回顧和今天的計劃 我對接下來的進展感到非常興奮。雖然我們可能會遇到一些問題&#xff0c;但昨天我們差不多完成了將所有內容遷移到新的日志系統的工作&#xff0c;我們正在把一些內容整合進來&#xff0c;甚至是之前通過不同方式記錄時間戳的舊平臺層部分&#xff0c;現在也…

Spring 如何處理循環依賴

在 Spring 框架里&#xff0c;循環依賴指的是多個 Bean 之間相互依賴&#xff0c;從而形成一個閉環。例如&#xff0c;Bean A 依賴 Bean B&#xff0c;而 Bean B 又依賴 Bean A。Spring 主要通過三級緩存機制來處理循環依賴&#xff0c;下面詳細介紹相關內容。 1. 三級緩存的定…

Android開發layer-list

Android開發layer-list 它的用處可以在drawable上進行多圖拼接&#xff0c;比如啟動頁&#xff0c;不想圖片被拉伸就這么做。還有做某些線突出來。 示例代碼&#xff1a; <?xml version"1.0" encoding"utf-8"?> <layer-list xmlns:android&q…

手機測試,工作中學習

要學習各種機型的截圖方式、開發模式在哪。 榮耀機型&#xff1a;截圖&#xff1a;關節快速敲兩下。開發者模式在“系統和更新”里。 1.出現缺陷&#xff0c;需要獲取日志。 學習adb生成日志&#xff1a;當測試中出現缺陷的&#xff0c;使用adb logcat -d > d:/log.txt …