最近想學習一下C++ STL中容器類的實現,就先研究最簡單的string容器類,通過學習簡單的,達到管中窺豹,舉一反三的目的,話不多說,直接來乾貨,寫得不好,望大家多多交流和指教。
代碼如下:
<span style="font-size:18px;">// MyString.cpp : 定義控制檯應用程序的入口點。
//
/*****************************************
**功能:自己實現C++ STL的string容器類
**作者:謝凡凡
**時間:2015-07-21 02:20
*****************************************/
#include "stdafx.h"
#include <iostream>
#include <iomanip> //後邊用到函數setw設置域寬,所以包含該頭文件
using namespace std;
//自己嘗試寫的一個string容器類
class MyString
{
friend ostream& operator<<(ostream&,MyString&); //輸出運算符重載,友元函數
friend istream& operator>>(istream&,MyString&); //輸入運算符重載
public:
MyString(const char* str = NULL); //默認構造函數,含有一個默認參數
MyString(const MyString& other); //拷貝構造函數,拷貝了數據,所以說深拷貝
MyString& operator=(const MyString& other); //重載賦值運算符
MyString operator+(const MyString& other) const; //重載加號運算符
bool operator==(const MyString& ); //operator==
bool operator<(const MyString& ); //operator<
char& operator[](int); //operator[]
size_t size() {return strlen(m_data);}
MyString& append(const MyString& other); //在尾部插入
MyString& insert(unsigned int ipos,const char *); //任意位置插入
MyString& replace(unsigned int ipos,unsigned int num,const char *); //替換操作
MyString& erase(unsigned int start,unsigned int final); //刪除函數
int find(const char* stem,int ipos = 0); //查找函數
int find_first_of(const char* stem,int ipos = 0); //查找字符串中第一個在指定串中出現的字符位置
int rfind(const char *stem,int ipos = -1); //反向查找,從左往右數ipos位做爲起始的位置,然後從右往左匹配,找到第一個返回位置
int npos; //查詢標誌 表示字符串查詢失敗
//下邊寫迭代器類
class Iterator
{
char *init;
public:
inline Iterator(char* init) {this->init = init;} //構造函數
inline bool operator!=(Iterator& it) {return this->init != it.init;} //迭代器的幾個運算符重載
inline void operator++(int){init = init + 1;}
inline char operator*() {return *init;}
};
char* Begin() {return m_data;} //獲得迭代器的起始位置
char* End(){return m_end;} //獲得迭代器的尾後位置
~MyString(); //析構函數
private:
char* m_data; //指向動態內存的指針
char* m_end; //尾後指針
};
inline MyString::MyString(const char* str) //默認構造函數設爲內聯 不掉用 直接替換
{
if (!str)
{
m_data =NULL;
m_end = NULL;
}
else
{
m_data = new char[strlen(str)+1];
m_end = m_data + strlen(str); //尾後迭代器位置
strcpy(m_data,str);
}
npos = -1;
}
inline MyString::MyString(const MyString& other) //拷貝函數
{
if (!other.m_data) //在類的成員函數內可以訪問同種對象的私有數據(同種類是友元關係)
{
m_data = NULL;
m_end = NULL;
}
else
{
m_data = new char[strlen(other.m_data)+1];
m_end = m_data + strlen(other.m_data);
strcpy(m_data,other.m_data);
}
npos = -1;
}
inline MyString& MyString::operator=(const MyString& other) //賦值運算符
{
if (this != &other) //注意,賦值運算符考慮自賦值
{
delete [] m_data;
m_end = m_data;
if (!other.m_data)
{
m_data = NULL;
m_end = NULL;
}
else
{
m_data = new char[strlen(other.m_data)+1];
m_end = m_data + strlen(other.m_data);
strcpy(m_data,other.m_data);
}
}
return *this; //this指針的引用爲類對象
}
inline MyString MyString::operator+(const MyString& other)const //加號重載
{
//考慮多種情況
MyString newString; //加號重載,返回一個值,所以用一個臨時變量返回
if (!other.m_data)
{
newString = *this;
}
else if (!m_data)
{
newString = other;
}
else
{
newString.m_data = new char[strlen(m_data) + strlen(other.m_data) +1];
newString.m_end = newString.m_data + strlen(m_data) + strlen(other.m_data);
strcpy(newString.m_data,m_data);
strcat(newString.m_data,other.m_data);
}
return newString;
}
inline bool MyString::operator==(const MyString& other)
{
if (strlen(other.m_data) != strlen(m_data))
{
return false;
}
else
{
return strcmp(other.m_data,m_data)?false:true;
}
}
inline bool MyString::operator<(const MyString& other) //operator<
{
if (strlen(m_data) == 0 && strlen(other.m_data) != 0)
{
return true;
}
else if (strlen(m_data) != 0 && strlen(other.m_data) == 0)
{
return false;
}
else if (strlen(m_data) == 0 && strlen(other.m_data) == 0) //表示兩個都爲空
{
return false;
}
int iIndex = 0;
while (m_data[iIndex] && other.m_data[iIndex]) //相同長度逐個比較字符
{
if (m_data[iIndex] < other.m_data[iIndex])
{
return true;
}
else if (m_data[iIndex] > other.m_data[iIndex])
{
return false;
}
else
{
++iIndex;
}
}
if (!m_data[iIndex] && other.m_data[iIndex]) //第一個字符串比第二個字符串短 但前邊相同 eg: xiefanfan xiefanfanaa
{
return true;
}
else
{
return false;
}
}
inline char& MyString::operator[](int num)
{
if (num < 0 || num >= strlen(m_data))
{
cout<<"string subscript out of range"<<endl;
}
if (num>=0 && num<strlen(m_data))
{
return m_data[num];
}
}
ostream& operator<<(ostream& os,MyString& mstem) //友元函數,表示該函數不是該類的成員,但是可以操作類的私有數據
{
os<<mstem.m_data; //不要添加格式控制符,只做輸出
return os; //return支持連續<<
}
istream& operator>>(istream& is,MyString& mstem)
{
char temp[255]; //臨時緩衝空間
is>>setw(255)>>temp;
mstem = temp; //使用賦值運算符
return is; //return支持連續>>
}
MyString::~MyString() //析構函數
{
if (m_data)
{
delete [] m_data;
m_data = NULL;
m_end = NULL;
}
}
MyString& MyString::append(const MyString& other) //在尾部插入 參考operator+函數
{
MyString newString; //申請一個臨時空間 將原始字符串保存起來
if (!other.m_data)
{
return *this;
}
else if (!m_data)
{
m_data = new char[strlen(other.m_data) + 1];
m_end = m_data + strlen(other.m_data);
strcpy(m_data,other.m_data);
return *this;
}
else
{
newString = *this;
m_data = new char[strlen(newString.m_data) + strlen(other.m_data) +1];
m_end = m_data + strlen(newString.m_data) + strlen(other.m_data);
strcpy(m_data,newString.m_data);
strcat(m_data,other.m_data);
return *this;
}
}
MyString& MyString::insert(unsigned int ipos,const char *stem) //任意位置插入函數
{
MyString newString;
int iIndex;
if (ipos >= 0 && ipos < strlen(m_data)) //ipos在範圍內部
{
newString.m_data = new char[strlen(m_data) + strlen(stem) +1]; //申請空間
newString.m_end = m_data + strlen(m_data) + strlen(stem);
for (iIndex = 0;iIndex < strlen(m_data) + strlen(stem);++iIndex)
{
if (iIndex < ipos) //拷貝原始串 ipos前的數據
{
newString.m_data[iIndex] = m_data[iIndex];
}
else
{
if (iIndex >= ipos && iIndex < (ipos + strlen(stem))) //添加子串
{
newString.m_data[iIndex] = stem[iIndex - ipos];
}
else //添加原來字符串後邊的串
{
newString.m_data[iIndex] = m_data[iIndex - strlen(stem)];
}
}
}
newString.m_data[iIndex] = NULL; //最後一位置空 表示字符串結束
}
*this = newString;
return *this;
}
MyString& MyString::replace(unsigned int ipos,unsigned int num,const char *stem) //替換操作
{
MyString newString; //臨時緩存字符串
int iIndex;
if (ipos >= 0 && ipos < strlen(m_data) )
{
int iNewlen = strlen(m_data) + strlen(stem) - num; //新的長度
newString.m_data = new char[iNewlen + 1];
newString.m_end = m_data + iNewlen;
for (iIndex = 0;iIndex < iNewlen;++iIndex)
{
if (iIndex < ipos) //拷貝ipos索引前的字符
{
newString.m_data[iIndex] = m_data[iIndex];
}
else if (iIndex >= ipos && iIndex < ipos + strlen(stem)) //拷貝替換的串
{
newString.m_data[iIndex] = stem[iIndex - ipos];
}
else //拷貝原串剩餘的部分
{
newString.m_data[iIndex] = m_data[iIndex - strlen(stem) + num];
}
}
}
newString.m_data[iIndex] = '\0'; //字符串最後置爲0
*this = newString;
return *this;
}
/////////////////////////////此處應該用的迭代器/////////////////////////////////////////////
MyString& MyString::erase(unsigned int start,unsigned int final) //刪除函數
{
if (start >= 0 && start < strlen(m_data) && final >= 0 && final < strlen(m_data) && start <= final)
{
int iIndex;
for (iIndex = final;iIndex < strlen(m_data);++iIndex )
{
m_data[iIndex - final + start] = m_data[iIndex]; //後邊覆蓋前邊
}
m_data[iIndex - (final - start)] = '\0';
m_end = m_data + iIndex - (final - start);
}
return *this;
}
int MyString::find(const char* stem,int ipos ) //字串查找函數
{
if (ipos + strlen(stem) > strlen(m_data)) //超出範圍
{
return npos;
}
for (int iIndex = ipos;iIndex < strlen(m_data) - strlen(stem);++iIndex) //在長的串中匹配指定的串 若要快速匹配 可以用KMP算法
{
int jval = 0;
while (stem[jval] && stem[jval] == m_data[iIndex + jval]) //逐位匹配
{
jval++;
}
if (jval >= strlen(stem))
{
return iIndex;
}
}
return npos;
}
int MyString::find_first_of(const char* stem,int ipos) //查找字符串中第一個在指定串中出現的字符位置
{
int length = strlen(m_data);
int iIndex;
for ( iIndex = 0;iIndex < length;++iIndex)
{
for (int iIndex1 = 0;iIndex1 < strlen(stem);++iIndex1)
{
if (m_data[iIndex] == stem[iIndex1]) //如果匹配上一個字符,返回座標
{
return iIndex;
}
}
}
if (iIndex >= length)
{
return npos;
}
}
int MyString::rfind(const char *stem,int ipos) //反向查找
{
if (ipos == npos)
{
ipos = strlen(m_data);
}
for (int iIndex = ipos;iIndex >= 0;--iIndex)
{
int slen = strlen(m_data); //原串長度
int slen1 = strlen(stem); //匹配串長度
if ((slen - iIndex) >= slen1) //當前字符後邊字符數大於等於帶匹配串
{
int tem = 0;
while (m_data[iIndex + tem] == stem[tem] && tem < slen1) //逐項匹配
{
++tem;
}
if (tem >= slen1)
{
return iIndex;
}
}
}
return npos; //如果上邊沒有匹配上,表示不存在匹配項
}
int _tmain(int argc, _TCHAR* argv[])
{
MyString str("fasfd");
str.erase(0,2);
MyString::Iterator start = str.Begin();
MyString::Iterator end = str.End();
while(start != end)
{
cout<<*start<<endl;
start++;
}
return 0;
}</span>
每一個函數經過測試,功能基本上跑通了,但是壓力測試可能過不了。
自我總結需要改進的地方:
(1)每一次大小改變都重新申請內存,導致效率不高,同時造成內存碎片。[改進:申請一塊內存,多申請一些,多餘的內存作爲保留字節]
(2)string的成員函數參數大部分都是字符串類型,應該還有輸入迭代器類型的很多重載函數,我也沒有實現。
(3)有部分成員函數爲實現,比如查找只實現了三個,還有像capacity(),reserve()沒有實現,也可以添加實現。
(4)函數加保護,超出範圍是拋出異常還是報錯,沒實現。[本想拋出異常的,但是還不太會,就沒做]