C++模擬實(shí)現(xiàn)string的示例代碼
一、std::swap和std::string::swap的區(qū)別
如果用std::swap交換兩個(gè)string對象,將會(huì)發(fā)生1次構(gòu)造和2次賦值,也就是三次深拷貝;而使用std::string::swap僅交換成員,代價(jià)較小。
二、string的默認(rèn)構(gòu)造函數(shù)
1、構(gòu)造函數(shù)
string(const char* s = "") { _size = strlen(s);//_size和_capacity均不包含'\0' _capacity = _size; _arr = new char[_size + 1]; memcpy(_arr, s, _size + 1); }
構(gòu)造函數(shù)用缺省值,能夠滿足空串的構(gòu)造。
這里設(shè)計(jì)_size和_capacity均不包含'\0'。_arr的空間多new一個(gè),用于儲(chǔ)存'\0'。
再將形參的內(nèi)存拷貝至_arr中,即可完成構(gòu)造。
2、拷貝構(gòu)造
寫法1:老老實(shí)實(shí)的根據(jù)string對象的私有變量進(jìn)行拷貝構(gòu)造。
string(const string& s) { _size = s._size;//_size和_capacity均不包含'\0' _capacity = s._capacity; _arr = new char[_capacity + 1]; memcpy(_arr, s._arr, _capacity + 1); }
寫法2:通過構(gòu)造一個(gè)臨時(shí)對象,將這個(gè)臨時(shí)對象的私有變量全部和*this的私有變量交換。
注意拷貝構(gòu)造需要先將_arr初始化為nullptr,防止后續(xù)tmp拿到隨機(jī)地址。(tmp銷毀將調(diào)用析構(gòu)函數(shù),對一塊隨機(jī)地址的空間進(jìn)行析構(gòu)程序?qū)?huì)崩潰)
void swap(string& s) { std::swap(_arr, s._arr); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } string(const string& s) :_arr(nullptr)//防止交換后tmp._arr為隨機(jī)值,析構(gòu)出錯(cuò) { string tmp(s.c_str());//構(gòu)造 swap(tmp); }
3、賦值運(yùn)算符重載
寫法1:同樣的老實(shí)人寫法。這種寫法要防止自己給自己賦值!
string& operator=(const string& s) { if (this != &s)//防止自己給自己賦值 { _size = s._size; _capacity = s._capacity; char* tmp = new char[_capacity + 1]; delete[] _arr; _arr = tmp; memcpy(_arr, s._arr, _capacity + 1); } return *this; }
寫法2:通過構(gòu)造臨時(shí)變量tmp,完成賦值。這種寫法無需擔(dān)心自己給自己賦值的情況,并且_arr無需初始化為nullptr。
void swap(string& s) { std::swap(_arr, s._arr); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } string& operator=(const string& s) { string tmp(s.c_str());//構(gòu)造 swap(tmp); return *this; }
4、析構(gòu)函數(shù)
~string() { _size = _capacity = 0; delete[] _arr; _arr = nullptr; }
三、string中的小接口
//string的size()接口 size_t size()const//右const修飾*this,這樣const和非const對象均可調(diào)用 { return _size; } //string的c_str()接口 const char* c_str()const { return _arr; } //string的capacity()接口 size_t capacity()const { return _capacity; } //string的clear()接口 void clear() { _arr[0] = '\0'; _size = 0; } //string的判空 bool empty()const { return _size == 0 ? false : true; }
如果函數(shù)形參不發(fā)生改變的,無腦加const修飾。
只有指針和引用會(huì)有const權(quán)限問題。
四、遍歷接口的實(shí)現(xiàn)
1、對operator[]進(jìn)行重載
char& operator[](size_t pos)//普通對象,可讀可寫 { assert(pos < _size); return _arr[pos]; } const char& operator[](size_t pos)const//const對象,僅讀 { assert(pos < _size); return _arr[pos]; }
讓字符串進(jìn)行下標(biāo)式的訪問,需要重載兩個(gè)operator[]函數(shù),正常對象去調(diào)可讀可寫,const對象調(diào)用只讀。
2、迭代器
typedef char* iterator; iterator begin() { return _arr; } iterator end()//end指向字符串的'\0' { return _arr + _size; }
string的迭代器是字符指針,寫完迭代器就可以用迭代器實(shí)現(xiàn)訪問、修改了。
范圍for的底層也是一個(gè)迭代器,但是范圍for底層只認(rèn)begin()和end(),如果和自己實(shí)現(xiàn)的迭代器接口名稱對不上,那么范圍for將無法使用。
五、reserve和resize
//sring的reserve接口, 如果預(yù)開空間小于現(xiàn)有空間,將不會(huì)改變?nèi)萘俊? void reserve(size_t n = 0) { if (n + 1 > _capacity) { char* tmp = new char[n + 1]; memset(tmp, '\0', n + 1); memcpy(tmp, _arr, _size); delete[] _arr; _arr = tmp; _capacity = n; } } //sring的resize接口 void resize(size_t n, char c) { //判斷n的大小 if (n > _capacity) { reserve(n); memset(_arr + _size, c, n - _size); _size = n; } else { _arr[n] = '\0'; _size = n; } }
reserve是擴(kuò)容,可以用于預(yù)開空間,防止頻繁的空間申請。申請一塊n+1大小的空間,將該空間全部初始化'\0',再將_arr中的數(shù)據(jù)拷貝至tmp中,釋放_(tái)arr,_arr指向tmp。
在resize中需要考慮_size擴(kuò)容和縮容的問題。
六、插入刪除查找相關(guān)接口
1、push_back、append、+=
string& push_back(const char c) { //判斷容量 if (_size == _capacity) { size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;//防止出現(xiàn)空串的情況 reserve(newCapacity); } _arr[_size++] = c; return *this; } string& append(const char* s) { //判斷容量 size_t len = strlen(s); if (_size + len > _capacity) { reserve(_size + len); } strcpy(_arr + _size, s); _size += len; return *this; } string& operator+=(const char c) { push_back(c); return *this; } string& operator+=(const char* s) { append(s); return *this; }
寫push_back要考慮到原對象為空串的情況(即_capacity為0)。
+=可以復(fù)用push_back和append。
2、insert和earse
string& insert(size_t pos, char c) { assert(pos < _size); //判斷容量 if (_size == _capacity) { reserve(_capacity + 1); } //挪動(dòng)數(shù)據(jù) for (size_t i = _size; i > pos; --i) { _arr[i] = _arr[i - 1]; } _arr[pos] = c; ++_size; return *this; } string& insert(size_t pos, const char* s) { size_t len = strlen(s); //判斷容量 if (len + _size > _capacity) { reserve(len + _size); } //挪動(dòng)數(shù)據(jù) for (size_t i = _size + len; i > pos + len - 1; --i) { _arr[i] = _arr[i - len]; } memcpy(_arr + pos, s, len); _size += len; return *this; } string& earse(size_t pos, size_t len = npos) { assert(pos < _size); //先判斷刪到底的情況 if (len == npos || pos + len >= _size) { _arr[pos] = '\0'; _size = pos; } else { memcpy(_arr + pos, _arr + pos + len, _size - pos - len); _size -= len; } return *this; }
insert接口在挪動(dòng)數(shù)據(jù)時(shí),從最后一個(gè)元素的后一個(gè)(后len個(gè))位置開始覆蓋,可以保證不出現(xiàn)size_t 類型越界的情況。
earse接口,需要分類討論字符串是否刪到底。
注意,這個(gè)pos是const static成員,C++語法中,只有指針和整型的const static成員是可以在類中進(jìn)行初始化的。
3、find
size_t find(const char c, size_t pos = 0)const { assert(pos < _size); for (size_t i = pos; i < _size; ++i) { if (_arr[i] == c) { return i; } } return npos; } size_t find(const char* s, size_t pos = 0)const { assert(pos < _size); const char* p = strstr(_arr, s); if (p != nullptr) { return _arr - p; } return npos; }
從指定位置找字符或字符串,找到了,返回第一個(gè)匹配字符/子串的下標(biāo)。
七、流插入和流提取
//流插入和流提取的重載時(shí)為了自定義類型的輸入輸出 inline ostream& operator<<(ostream& out, const string& s)//這里訪問的到私有,所以可以不用寫成友元函數(shù) { for (size_t i = 0; i < s.size(); ++i)//流插入按照_size打印,c_str找到'\0'結(jié)束打印 { //比如我在字符串中間插入一個(gè)'\0',打印結(jié)果不一樣 out << s[i]; } return out; } inline istream& operator>>(istream& in, string& s) { s.clear();//用之前先清空s //in >> c;//流提取不會(huì)識(shí)別空格和換行 char c = in.get(); char buff[128] = { '\0' };//防止頻繁擴(kuò)容 size_t i = 0; while (c != ' ' && c != '\n') { if (i == 127) { s += buff; i = 0; } buff[i++] = c; c = in.get(); } if (i > 0) { buff[i] = '\0'; s += buff; } return in; }
因?yàn)閟tring提供了訪問私有的接口,所以流插入和流提取可以不用重載成string類的友元函數(shù)。
對于流提取,如果頻繁的尾插,會(huì)造成頻繁擴(kuò)容。而且C++的擴(kuò)容和C語言的擴(kuò)容不一樣,C++使用new不能原地?cái)U(kuò)容,只能異地?cái)U(kuò)容,異地?cái)U(kuò)容就會(huì)導(dǎo)致新空間的開辟、數(shù)據(jù)的拷貝、舊空間釋放。為了防止頻繁擴(kuò)容,我們可以創(chuàng)建一個(gè)可以存儲(chǔ)128字節(jié)的數(shù)組,在這個(gè)數(shù)組中操作,這個(gè)數(shù)組滿了就尾插至對象s中。
為什么不能用getline,而是要一個(gè)字符一個(gè)字符尾插呢?因?yàn)榱魈崛∮龅娇崭窈?#39;\n'會(huì)結(jié)束提取,剩余數(shù)據(jù)暫存緩沖區(qū),如果是getline的話,遇到空格是不會(huì)停止讀取的。
八、模擬實(shí)現(xiàn)的string整體代碼
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include <assert.h> using std::cout; using std::cin; using std::endl; using std::ostream; using std::istream; namespace jly { class string { public: void swap(string& s) { std::swap(_arr, s._arr); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } //構(gòu)造函數(shù) string(const char* s = "") { _size = strlen(s);//_size和_capacity均不包含'\0' _capacity = _size; _arr = new char[_size + 1]; memcpy(_arr, s, _size + 1); } //拷貝構(gòu)造 //寫法1 //string(const string& s) //{ // _size = s._size;//_size和_capacity均不包含'\0' // _capacity = s._capacity; // _arr = new char[_capacity + 1]; // memcpy(_arr, s._arr, _capacity + 1); //} //寫法2 string(const string& s) :_arr(nullptr)//防止交換后tmp._arr為隨機(jī)值,析構(gòu)出錯(cuò) { string tmp(s.c_str());//構(gòu)造 swap(tmp); } //賦值運(yùn)算符重載 //寫法1 //string& operator=(const string& s) //{ // if (this != &s)//防止自己給自己賦值 // { // _size = s._size; // _capacity = s._capacity; // char* tmp = new char[_capacity + 1]; // delete[] _arr; // _arr = tmp; // memcpy(_arr, s._arr, _capacity + 1); // } // return *this; //} //寫法2 string& operator=(const string& s) { string tmp(s.c_str());//構(gòu)造 swap(tmp); return *this; } //析構(gòu)函數(shù) ~string() { _size = _capacity = 0; delete[] _arr; _arr = nullptr; } //string的size()接口 size_t size()const//右const修飾*this,這樣const和非const對象均可調(diào)用 { return _size; } //string的c_str()接口 const char* c_str()const { return _arr; } //string的capacity()接口 size_t capacity()const { return _capacity; } //string的clear()接口 void clear() { _arr[0] = '\0'; _size = 0; } //string的判空 bool empty()const { return _size == 0 ? false : true; } //對operator[]進(jìn)行重載 char& operator[](size_t pos)//普通對象,可讀可寫 { assert(pos < _size); return _arr[pos]; } const char& operator[](size_t pos)const//const對象,僅讀 { assert(pos < _size); return _arr[pos]; } //迭代器 typedef char* iterator; iterator begin()const { return _arr; } iterator end()const//end指向字符串的'\0' { return _arr + _size ; } //string的reserve接口,如果預(yù)開空間小于現(xiàn)有空間,將不會(huì)改變?nèi)萘俊? void reserve(size_t n=0) { if (n + 1 > _capacity) { char* tmp = new char[n + 1]; memset(tmp, '\0', n + 1); memcpy(tmp, _arr, _size); delete[] _arr; _arr = tmp; _capacity = n; } } //string的resize接口 void resize(size_t n, char c='\0') { //判斷n的大小 if (n > _capacity) { reserve(n); memset(_arr + _size,c,n-_size); _size = n; } else { _arr[n] = '\0'; _size = n; } } //插入刪除查找相關(guān)接口 string& push_back(const char c) { //判斷容量 if (_size == _capacity) { size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;//防止出現(xiàn)空串的情況 reserve(newCapacity); } _arr[_size++] = c; return *this; } string& append(const char* s) { //判斷容量 size_t len = strlen(s); if (_size + len > _capacity) { reserve(_size + len); } strcpy(_arr+_size,s); _size += len; return *this; } string& operator+=(const char c) { push_back(c); return *this; } string& operator+=(const char* s) { append(s); return *this; } string& insert(size_t pos, char c) { assert(pos < _size); //判斷容量 if (_size == _capacity) { reserve(_capacity + 1); } //挪動(dòng)數(shù)據(jù) for (size_t i = _size; i > pos; --i) { _arr[i] = _arr[i - 1]; } _arr[pos] = c; ++_size; return *this; } string& insert(size_t pos, const char* s) { size_t len = strlen(s); //判斷容量 if (len + _size > _capacity) { reserve(len + _size); } //挪動(dòng)數(shù)據(jù) for (size_t i = _size + len; i > pos + len - 1; --i) { _arr[i] = _arr[i - len]; } memcpy(_arr + pos, s, len); _size += len; return *this; } string& earse(size_t pos, size_t len = npos) { assert(pos<_size); //先判斷刪到底的情況 if (len == npos || pos + len >= _size) { _arr[pos] = '\0'; _size = pos; } else { memcpy(_arr + pos, _arr + pos + len,_size-pos-len); _size -= len; } return *this; } size_t find(const char c, size_t pos = 0)const { assert(pos < _size); for (size_t i = pos; i < _size; ++i) { if (_arr[i] == c) { return i; } } return npos; } size_t find(const char* s, size_t pos = 0)const { assert(pos < _size); const char* p = strstr(_arr, s); if (p != nullptr) { return _arr - p; } return npos; } private: char* _arr; size_t _size; size_t _capacity; const static size_t npos = -1;//只有const static整型、指針成員變量可以在類中定義,其他類型不行 }; //流插入和流提取的重載時(shí)為了自定義類型的輸入輸出 inline ostream& operator<<(ostream& out, const string& s)//這里訪問得到私有,所以可以不用寫成友元函數(shù) { for (size_t i = 0; i < s.size(); ++i)//流插入按照_size打印,c_str找到'\0'結(jié)束打印 { //比如我在字符串中間插入一個(gè)'\0',打印結(jié)果不一樣 out << s[i]; } return out; } inline istream& operator>>(istream& in, string& s) { s.clear();//用之前先清空s //in >> c;//流提取不會(huì)識(shí)別空格和換行 char c=in.get(); char buff[128] = { '\0' };//防止頻繁擴(kuò)容 size_t i = 0; while (c != ' ' && c != '\n') { if (i == 127) { s += buff; i = 0; } buff[i++] = c; c = in.get(); } if (i > 0) { buff[i] = '\0'; s += buff; } return in; } //測試函數(shù) void test1() { } }
以上就是C++模擬實(shí)現(xiàn)string的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C++實(shí)現(xiàn)string的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Visual Studio調(diào)試C/C++教程指南
VisualStudio是微軟開發(fā)的一款集成開發(fā)環(huán)境軟件,本文主要介紹了Visual Studio調(diào)試C/C++教程指南,熟悉地掌握基于VS的C/C++調(diào)試技術(shù),可以大幅提升調(diào)試性能,感興趣的可以了解一下2024-06-06C/C++ Qt 自定義Dialog對話框組件應(yīng)用案例詳解
有時(shí)候我們需要一次性修改多個(gè)數(shù)據(jù),使用默認(rèn)的模態(tài)對話框似乎不太夠用,此時(shí)我們需要自己創(chuàng)建一個(gè)自定義對話框。這篇文章主要介紹了Qt自定義Dialog對話框組件的應(yīng)用,感興趣的同學(xué)可以學(xué)習(xí)一下2021-11-11C/C++中一次性執(zhí)行多個(gè)DOS命令的實(shí)現(xiàn)思路
在C語言中執(zhí)行DOS命令的方法很多,在這就不一給大家一一介紹了,本文重點(diǎn)給大家介紹C/C++中一次性執(zhí)行多個(gè)DOS命令的實(shí)現(xiàn)思路,需要的朋友參考下2017-12-12C++ Boost Random隨機(jī)函數(shù)詳解
Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個(gè)可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱2022-11-11