C++模擬實(shí)現(xiàn)string的示例代碼
一、std::swap和std::string::swap的區(qū)別
如果用std::swap交換兩個(gè)string對(duì)象,將會(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對(duì)象的私有變量進(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í)對(duì)象,將這個(gè)臨時(shí)對(duì)象的私有變量全部和*this的私有變量交換。
注意拷貝構(gòu)造需要先將_arr初始化為nullptr,防止后續(xù)tmp拿到隨機(jī)地址。(tmp銷毀將調(diào)用析構(gòu)函數(shù),對(duì)一塊隨機(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對(duì)象均可調(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、對(duì)operator[]進(jìn)行重載
char& operator[](size_t pos)//普通對(duì)象,可讀可寫
{
assert(pos < _size);
return _arr[pos];
}
const char& operator[](size_t pos)const//const對(duì)象,僅讀
{
assert(pos < _size);
return _arr[pos];
}
讓字符串進(jìn)行下標(biāo)式的訪問,需要重載兩個(gè)operator[]函數(shù),正常對(duì)象去調(diào)可讀可寫,const對(duì)象調(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)的迭代器接口名稱對(duì)不上,那么范圍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ù)開空間,防止頻繁的空間申請(qǐng)。申請(qǐng)一塊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要考慮到原對(duì)象為空串的情況(即_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ù)。
對(duì)于流提取,如果頻繁的尾插,會(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ù)組滿了就尾插至對(duì)象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對(duì)象均可調(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;
}
//對(duì)operator[]進(jìn)行重載
char& operator[](size_t pos)//普通對(duì)象,可讀可寫
{
assert(pos < _size);
return _arr[pos];
}
const char& operator[](size_t pos)const//const對(duì)象,僅讀
{
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;
}
//測(cè)試函數(shù)
void test1()
{
}
}以上就是C++模擬實(shí)現(xiàn)string的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C++實(shí)現(xiàn)string的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c語言輸出字符串中最大對(duì)稱子串長(zhǎng)度的3種解決方案
這篇文章主要介紹了c語言輸出字符串中最大對(duì)稱子串長(zhǎng)度的3種解決方案,需要的朋友可以參考下2014-03-03
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-06
C/C++ Qt 自定義Dialog對(duì)話框組件應(yīng)用案例詳解
有時(shí)候我們需要一次性修改多個(gè)數(shù)據(jù),使用默認(rèn)的模態(tài)對(duì)話框似乎不太夠用,此時(shí)我們需要自己創(chuàng)建一個(gè)自定義對(duì)話框。這篇文章主要介紹了Qt自定義Dialog對(duì)話框組件的應(yīng)用,感興趣的同學(xué)可以學(xué)習(xí)一下2021-11-11
C/C++中一次性執(zhí)行多個(gè)DOS命令的實(shí)現(xiàn)思路
在C語言中執(zhí)行DOS命令的方法很多,在這就不一給大家一一介紹了,本文重點(diǎn)給大家介紹C/C++中一次性執(zhí)行多個(gè)DOS命令的實(shí)現(xiàn)思路,需要的朋友參考下2017-12-12
C++ 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

