最近在重溫C++ Effective系列,在more effective C++中Item28~30涉及一個string類的實現,爲了深入地理解,故由簡單到複雜實現一個簡易的string類,這一篇裏,不考慮書中的3個條款,在後續的博客裏,逐步將條款的內容加入進來,實現一些相對複雜的特性;
string類聲明:
class String
{
friend std::ostream& operator<<(std::ostream& os, const String&);
friend std::istream& operator>>(std::istream& is, String&);
friend bool operator < (const String& left_, const String& right_);
friend bool operator > (const String& left_, const String& right_);
friend bool operator != (const String& left_, const String& right_);
friend bool operator == (const String& left_, const String& right_);
friend String operator + (const String& left_, const String& right_);
public:
String(const char* str_ = nullptr);
String(const String& str_);
String& operator=(const String& str_);
String& operator=(const char* str_);
~String();
public:
inline size_t size() const;
inline const char* c_str() const;
inline void swap(String& str_);
public:
String& operator += (const String&);
String& operator += (const char*);
const char& operator[](size_t index_) const;
char& operator[](size_t index_);
private:
char* _ptr;
size_t _size;
};
string類實現:
String::String(const char* str_/* = nullptr */)
{
if (str_ == nullptr)
{
_ptr = new char[1];
_ptr[0] = '\0';
_size = 0;
}
else
{
size_t len = strlen(str_) + 1;
_ptr = new char[len];
strcpy(_ptr, str_);
_size = len - 1;
}
}
String::String(const String& str_)
: String(str_._ptr)
//調用上個構造函數完成構造,降低代碼重複
{
/*_ptr = new char[str_._size + 1];
strcpy(_ptr, str_._ptr);
_size = str_._size;*/
}
String& String::operator=(const String& str_)
{
if (this == &str_)
{
return *this;
}
return operator=(str_._ptr);
}
String& String::operator=(const char* str_)
{
//異常安全
/*String tmp(str_);
swap(tmp);*/
swap(String(str_));
return *this;
}
String::~String()
{
if (_ptr != nullptr)
{
delete[] _ptr;
_ptr = nullptr;
_size = 0;
}
}
size_t String::size() const
{
return _size;
}
const char* String::c_str() const
{
return _ptr;
}
void String::swap(String& str_)
{
std::swap(_ptr, str_._ptr);
std::swap(_size, str_._size);
}
String& String::operator+=(const String& str_)
{
return operator+=(str_.c_str());
}
String& String::operator+=(const char* str_)
{
//strcpy(dst, src)函數,需保證src不爲nullptr
if (str_ == nullptr || *str_ == '\0')
{
return *this;
}
//異常安全
size_t len = _size + strlen(str_) + 1;
char* tmp = new char[len];
strcpy(tmp, _ptr);
strcat(tmp, str_);
std::swap(_ptr, tmp);
_size = len - 1;
delete[] tmp;
return *this;
}
const char& String::operator[](size_t index_) const
{
if (index_ >= _size)
{
throw std::out_of_range("String : out of index!");
}
return _ptr[index_];
}
//調用const版本的operator[],降低代碼重複
//反過來,不建議用const調用non-const,這
//樣可能會導致潛在的bug
char& String::operator[](size_t index_)
{
return const_cast<char&>(
(static_cast<const String&>(*this))[index_]);
}
std::ostream& operator<<(std::ostream& os, const String& str_)
{
os << str_._ptr;
return os;
}
std::istream& operator>>(std::istream& is, String& str_)
{
//預分配1個字節
char* str = (char*)malloc(sizeof(char) * 1);
size_t capacity = 1; //記錄當前分配空間的容量
char* buff = str;
//預處理流的所有空格與回車
//*buff = getchar() 或者is.get(*buff)均可
while ((*buff = getchar()) == ' ' || *buff == '\n');
//while (is.get(*buff) && (*buff == ' ' || *buff == '\n'));
int index = 1;
while (1)
{
//回車跳出
if (*buff == '\n')
{
*buff = '\0';
break;
}
//空格跳出
else if (*buff == ' ')
{
*buff = '\0';
break;
}
//空間不足
else if (index == capacity)
{
//容量加倍,並分配對應容量的空間
capacity *= 2;
str = (char*)realloc(str, capacity);
}
//爲了避免realloc返回首地址改變,不使用++buff,而是str+偏移量
buff = (str + index);
*buff = getchar();
//is.get(*buff); //每次讀取一個字符
index++;
}
//輸入完成
str_ = String(str);
free(str);
str = nullptr;
buff = nullptr;
return is;
}
bool operator < (const String& left_, const String& right_)
{
bool less = true;
int index = 1;
for (; index < left_.size() && index < right_.size(); ++index)
{
if (left_[index] != right_[index])
{
less = left_[index] < right_[index];
break;
}
}
if (index == left_.size() || index == right_.size())
{
less = left_.size() < right_.size();
}
return less;
}
bool operator > (const String& left_, const String& right_)
{
return (left_ != right_) && !(left_ < right_);
}
bool operator == (const String& left_, const String& right_)
{
if (left_.size() != right_.size())
{
return false;
}
bool equal = true;
for (int i = 0; i < left_.size(); ++i)
{
if (left_[i] != right_[i])
{
equal = false;
break;
}
}
return equal;
}
bool operator != (const String& left_, const String& right_)
{
return !(left_ == right_);
}
String operator + (const String& left_, const String& right_)
{
String tmp(left_);
tmp += right_;
return tmp;
}
需要注意的地方:
1.賦值運算符重載裏,需要判斷賦值對象是否是自身,不過,在我的實現中,通過swap函數保證異常安全,即使沒有以上的判斷,也能正常運行;
2.friend std::istream& operator>>(std::istream& is, String&)函數,實現不能是簡單的如下代碼:
friend std::istream& operator>>(std::istream& is, String& str_)
{
is>>str_._ptr;
return is;
}
因爲,參數str_可能是一個通過默認構造函數構造的對象,其 ptr指針指向的空間僅有一個字符(’\0’),is>>str . _ptr向這個空間寫入數據,會導致溢出,在VS2013平臺,雖然該函數能”正常”運行,但在該String對象被析構時,會因delete[] _ptr而出錯;
3.爲了最大可能消除代碼冗餘,函數執行效率會下降,例如operator>就需要調用operator<和operator=,這裏是在代碼的冗餘度,維護難度和執行效率間的協調間偏向前兩者;
4.friend std::istream& operator>>(std::istream& is, String&)的實現參考瞭如下鏈接:
https://zhidao.baidu.com/question/711251936107526885.html
5.operator[]函數,通過non-const版本調用const版本,消除代碼冗餘,這是在某本書上學習到的方法(具體書名忘記了);
由於在C++上實踐不多,加上算法能力弱,如果有實現的代碼有bug或者設計問題,希望可以評論中提出來,非常感謝^-^