C++ 簡易string類實現(一)

最近在重溫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或者設計問題,希望可以評論中提出來,非常感謝^-^

發佈了23 篇原創文章 · 獲贊 10 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章