中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++怎么實現String類

發布時間:2022-08-24 11:26:02 來源:億速云 閱讀:139 作者:iii 欄目:開發技術

這篇文章主要介紹了C++怎么實現String類的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇C++怎么實現String類文章都會有所收獲,下面我們一起來看看吧。

    string模擬實現

    string簡單實現

    首先我們不考慮string類的增刪查改,只是先給string類搭建一個最簡單的框架出來。

    和C語言中相同,為了存儲一個字符串,我們的string類需要一個char*的指針來指向字符像這個對象。作為一個對象,string還需要有構造函數,析構函數和拷貝構造。

    class string
    {
    private:
    	char *_str;
    public:
    	string(const char *str)
    		: _str(new char[strlen(str) + 1]) // +1 是給'\0'留出位置
    	{
    		strcpy(_str, str);
    	}
    
    	string(const string &str)
    		: _str(new char[strlen(str._str) + 1])
    	{
    		strcpy(_str, str._str);
    	}
    	~string()
    	{
    		if (_str)
    		{
    			delete[] _str;
    			_str = nullptr;
    		}
    	}
    };

    有的朋友可能會疑惑,這里的構造函數和拷貝構造函數為什么不用編譯器自動生成的,直接將_str指向原本的字符串就可以了,為什么還要開辟空間呢?

    這是因為我們在日常使用中,假如有兩個string類 a 和 b,b是由a拷貝構造而來,一般情況下我們在修改b的同時不希望a也被改。此外,如果直接將_str指向原本的字符串會導致的問題是當 a 和 b用完被銷毀時,會對同一片空間調用兩次析構函數,對同一片空間釋放兩次。所以在這里,我們需要重新開辟一片空間來給這個string。這也就是所謂的深拷貝。

    然后,為了訪問string類中的元素,我們需要對運算符[]進行重載。

    char& operator[](size_t pos)
    {
        assert(pos < strlen())
        return _str[pos];
    }

    這樣我們就實現了一個簡單的string類。

    string完整實現

    構造函數,析構函數,拷貝構造

    之前我們實現的一個string類是一個最簡單的string類,它沒有辦法進行增刪查改,接下來我們就來一點一點完善它。

    要實現增刪查改,我們還需要兩個變量,一個記錄string類當前長度,一個記錄string類的容量大小。加入這兩個變量后,我們原本的構造函數,拷貝構造和析構函數需要發生一點點變化。

    class string
    {
    private:
    	char *_str;
    	size_t _size;
    	size_t _capacity;
    
    public:
    	string(const char *str = "")
    		: _size(strlen(str)), _capacity(_size)
    	{
    		_str = new char[_capacity + 1];
    		strcpy(_str, str);
    	}
    
        string(const string &str)
            : _size(str._size), _capacity(str._capacity)
        {
            _str = new char[_size + 1];
            strcpy(_str, str._str);
        }
        
    	~string()
    	{
    		if (_str)
    		{
    			delete[] _str;
    			_str = nullptr;
    			_size = _capacity = 0;
    		}
    	}
    };

    運算符重載

    接下來我們來實現一下,string類的運算符。在實現運算符重載時,我們需要做的只是實現少數幾個運算符即可,其他的運算符可復用前面實現的運算符來達到我們想要的效果。

    //關系運算符的重載
    bool operator>(const string &s)
    {
        return strcmp(_str, s.c_str());
    }
    
    bool operator==(const string &s)
    {
        return strcmp(_str, s.c_str()) == 0;
    }
    
    bool operator!=(const string &s)
    {
        return !(*this == s);
    }
    
    bool operator>=(const string &s)
    {
        return *this > s || *this == s;
    }
    
    bool operator<(const string &s)
    {
        return !(*this >= s);
    }
    
    bool operator<=(const string &s)
    {
        return !(*this > s);
    }
    //操作運算符的重載
    string &operator=(string& str)
    {
        if(*this != str)
        {
            char *tmp = new char[str._capacity + 1];
            strcpy(tmp,str._str);
            delete[] _str;
            _str = tmp;
            _size = str._size;
            _capacity = str._capacity;
        }
        return *this;
    }
    
    char &operator[](size_t pos)
    {
        assert(pos < _size);
    
        return *(_str + pos);
    }
    
    const char &operator[](size_t pos) const
    {
        assert(pos < _size);
        return *(_str + pos);
    }

    string接口實現

    首先是比較簡單的size(),empty(),capacity(),clear()。這些接口大部分直接訪問string類的成員變量就可以得到結果。

    size_t size() const
    {
        return _size;
    }
    
    size_t capacity() const
    {
        return _capacity;
    }
    
    bool empty() const
    {
        return 0 == _size;
    }
    //后面添加const的目的是為了讓所有對象都可以進行訪問
    void clear()
    {
        _str[0] = '\0';
        _size = 0;
        _capacity = 0;
    }

    因為后面的接口大部分都需要進行空間的調整,所以首先我們將調整空間的接口,reserve和resize實現。

    void reserve(size_t n)
    {
        if (n > _capacity) //判斷是否需要擴容
        {
            char *tmp = new char[n + 1];
            strcpy(tmp, _str);
            delete[] _str;
            _str = tmp;
            _capacity = n;
        }
    }
    
    //resize和reserve的區別在于,reserve只是開空間,而resize還要進行初始化
    void resize(size_t n, char c = '\0')
    {
        if (n > _capacity)
        {
            reserve(n); //開空間復用reserve
        }
        for (size_t i = _size; i < n; ++i)
        {
            _str[i] = c;
        }
        _size = n;
        _str[_size] = '\0';
    }

    接下來是插入的實現,首先是push_back,這個比較簡單,找到尾部進行插入即可。

    void push_back(char n)
    {
        if (_size == _capacity)
        {
            reserve(_capacity == 0 ? 4 : _capacity * 2); //開空間復用reserve
        }
        _str[_size++] = n;
        _str[_size] = '\0';
    }

    接下來是insert,這個較push_back而言要麻煩一些,因為除了尾插,其他地方去插入數據你都需要挪動后面數據的位置。

    string &insert(size_t pos, const char *str)
    {
        //檢查空間是否足夠
        assert(pos <= _size);
        size_t len = strlen(str);
        if (len + _size > _capacity)
        {
            reserve(len + _size);
        }
    
       	//挪動后面的數據
        size_t end = _size + len;
        while (end != pos + len - 1)
        {
            _str[end] = _str[end - len];
            --end;
        }
    
        //數據插入
        strncpy(_str + pos, str, len);
        _size += len;
        return *this;
    }

    寫完了插入,接下來當然就是刪除接口:eraser

    string &eraser(size_t pos, size_t len = npos) //npos為靜態變量,值為-1
    {
        assert(pos < _size);
        
        if (len == npos || pos + len >= _size) //將位置后的元素全部刪除
        {
            _str[pos] = '\0';
            _size = pos;
        }
        else //刪除位置后的部分元素
        {
            size_t begin = pos + len;
            while (begin <= _size)
            {
                _str[begin - len] = _str[begin];
                begin++;
            }
            _size = _size - len;
        }
        return *this;
    }

    迭代器的實現

    C++中的迭代器和指針類似。為什么要有迭代器呢?因為C++中有各種各樣的容器,每個容器它背后的存儲方式不同,訪問方式也不同,為了讓使用者的使用成本降低,使大部分容器可以以相同的方式去訪問,就有了迭代器的產生。

    接下來我們來實現string的迭代器,其實string的迭代器就是一個指針。并不用去封裝特別的東西。

    typedef char *iterator;
    typedef const char *const_iterator;
    
    const_iterator begin() const
    {
        return _str;
    }
    
    const_iterator end() const
    {
        return _str + _size;
    }
    
    iterator begin()
    {
        return _str;
    }
    
    iterator end()
    {
        return _str + _size;
    }

    部分函數優化和完善

    前面在寫運算符重載時,還有部分運算符未重載在此加上

    string &operator+=(const char *str)
    {
        append(str);
    }
    
    string &operator+=(char n)
    {
        push_back(n);
        return *this;
    }

    同時增加拷貝構造和operator=的現代寫法,之前我們寫拷貝構造和operator=時都需要自己去重新開空間,那么這個活可不可以讓其他人幫我做呢?

    我們來看看下面這一段代碼

    void swap(string& str)
    {
        std::swap(_str, str._str);
        std::swap(_size, str._size);
        std::swap(_capacity, str._capacity);
    }
    
    string(const string &s)
        : _str(nullptr), _size(0), _capacity(0)
    {
        string tmp(s._str);
        swap(tmp);
    }
    
    string &operator=(string s)
    {
        swap(s);
        return *this;
    }

    上述代碼同樣可以幫我們完成拷貝構造和operator= ,原理如下:

    1.首先是拷貝構造,我們在拷貝構造中使用構造函數去創建一個臨時對象,這個臨時對象在創建時,就幫我們開辟了空間。然后我們將臨時對象和此對象的所有成員進行一個交換,這樣此對象就可以接管臨時對象創建的那塊空間,我們的拷貝構造也就成功了

    2.在operator=這,我們使用的是傳值傳參。好處在于由于我們的string類是自定義對象,所以在傳參時會去調用拷貝構造,這樣傳過來的str參數也擁有了自己的空間,此時我們和拷貝構造一樣,將str所開辟的那塊空間接管,同時由于str是函數參數,當函數結束時,str會去調用析構函數進行一個空間釋放。

    完整代碼

    class string
    {
    public:
        typedef char *iterator;
        typedef const char *const_iterator;
    
        const_iterator begin() const
        {
            return _str;
        }
    
        const_iterator end() const
        {
            return _str + _size;
        }
    
        iterator begin()
        {
            return _str;
        }
    
        iterator end()
        {
            return _str + _size;
        }
    
        string(const char *s = "")
            : _size(strlen(s)),
              _capacity(_size)
        {
            _str = new char[_capacity + 1];
            strcpy(_str, s);
        }
    
        string(const string &s)
            : _str(nullptr),
              _size(0),
              _capacity(0)
        {
            string tmp(s._str);
            swap(tmp);
        }
    
        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _size = _capacity = 0;
        }
    
        string &operator=(string s)
        {
            swap(s);
            return *this;
        }
    
        char &operator[](size_t pos)
        {
            assert(pos < _size);
    
            return *(_str + pos);
        }
    
        const char &operator[](size_t pos) const
        {
            assert(pos < _size);
            return *(_str + pos);
        }
    
        const char *c_str() const
        {
            return _str;
        }
    
        void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char *tmp = new char[n + 1];
                strcpy(tmp, _str);
                delete[] _str;
                _str = tmp;
                _capacity = n;
            }
        }
    
        void push_back(char n)
        {
            if (_size == _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }
            _str[_size++] = n;
            _str[_size] = '\0';
        }
    
        string &operator+=(char n)
        {
            push_back(n);
            return *this;
        }
    
        void append(const char *str)
        {
            size_t len = _size + strlen(str);
            if (len > _capacity)
            {
                reserve(len);
            }
            strcpy(_str + _size, str);
            _size = len;
        }
    
        string &operator+=(const char *str)
        {
            append(str);
        }
    
        void resize(size_t n, char c = '\0')
        {
            if (n > _capacity)
            {
                reserve(n);
            }
            for (size_t i = _size; i < n; ++i)
            {
                _str[i] = c;
            }
            _size = n;
            _str[_size] = '\0';
        }
    
        size_t size() const
        {
            return _size;
        }
    
        size_t capacity() const
        {
            return _capacity;
        }
    
        bool empty()
        {
            return 0 == _size;
        }
    
        bool operator>(const string &s)
        {
            return strcmp(_str, s.c_str());
        }
    
        bool operator==(const string &s)
        {
            return strcmp(_str, s.c_str()) == 0;
        }
    
        bool operator!=(const string &s)
        {
            return !(*this == s);
        }
    
        bool operator>=(const string &s)
        {
            return *this > s || *this == s;
        }
    
        bool operator<(const string &s)
        {
            return !(*this >= s);
        }
    
        bool operator<=(const string &s)
        {
            return !(*this > s);
        }
    
        string &insert(size_t pos, const char *str)
        {
            assert(pos <= _size);
            size_t len = strlen(str);
            if (len + _size > _capacity)
            {
                reserve(len + _size);
            }
    
            size_t end = _size + len;
            while (end != pos + len - 1)
            {
                _str[end] = _str[end - len];
                --end;
            }
    
            strncpy(_str + pos, str, len);
            _size += len;
            return *this;
        }
    
        string &eraser(size_t pos, size_t len = npos)
        {
            assert(pos < _size);
    
            if (len == npos || pos + len >= _size)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
                size_t begin = pos + len;
                while (begin <= _size)
                {
                    _str[begin - len] = _str[begin];
                    begin++;
                }
                _size = _size - len;
            }
            return *this;
        }
    
        void clear()
        {
            _size = 0;
            _str[0] = '\0';
            _capacity = 0;
        }
    
        void swap(string &s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }
    
        size_t find(char c, size_t pos = 0) const
        {
            while (pos < _size)
            {
                if (_str[pos] == c)
                {
                    return pos;
                }
                ++pos;
            }
            return npos;
        }
    
        size_t find(char *s, size_t pos = 0) const
        {
            const char *p = strstr(_str + pos, s);
            if (p == nullptr)
            {
                return npos;
            }
            else
            {
                return p - _str;
            }
        }
    
    private:
        char *_str;
        size_t _size;
        size_t _capacity;
        const static size_t npos;
    };
    
    const size_t string::npos = -1;

    關于“C++怎么實現String類”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“C++怎么實現String類”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    墨竹工卡县| 中卫市| 呈贡县| 兴山县| 株洲市| 历史| 揭西县| 山阴县| 贵港市| 理塘县| 宁晋县| 门头沟区| 界首市| 和平县| 岐山县| 金秀| 新竹市| 新闻| 清苑县| 唐山市| 肇州县| 永年县| 淳化县| 安福县| 光山县| 遵化市| 东阳市| 隆林| 桦南县| 日土县| 朝阳市| 阳江市| 都兰县| 泗阳县| 临夏县| 依兰县| 长兴县| 枞阳县| 康定县| 吉隆县| 贵港市|