日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

C++實(shí)現(xiàn)String類的方法詳解_C 語言

作者:。菀枯。 ? 更新時(shí)間: 2022-10-17 編程語言

前言

在C語言中,沒有專門用來表示字符串的類型。C語言的字符串是一系列以’\0’為結(jié)尾的字符的集合。雖然C語言為這樣的字符串提供了一系列的庫函數(shù)如strcpy, strcmp等等,但這些函數(shù)與字符串這個(gè)類型是分開的,這不太符合C++中面試對象的思想,所以在C++中封裝了一個(gè)string類,來幫助我們操作字符串。string該如何使用,我這里就不做贅述了,大家可以去看看官方文檔呀

string - C++ Reference (cplusplus.com)

string模擬實(shí)現(xiàn)

string簡單實(shí)現(xiàn)

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

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

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;
		}
	}
};

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

這是因?yàn)槲覀冊谌粘J褂弥校偃缬袃蓚€(gè)string類 a 和 b,b是由a拷貝構(gòu)造而來,一般情況下我們在修改b的同時(shí)不希望a也被改。此外,如果直接將_str指向原本的字符串會(huì)導(dǎo)致的問題是當(dāng) a 和 b用完被銷毀時(shí),會(huì)對同一片空間調(diào)用兩次析構(gòu)函數(shù),對同一片空間釋放兩次。所以在這里,我們需要重新開辟一片空間來給這個(gè)string。這也就是所謂的深拷貝。

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

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

這樣我們就實(shí)現(xiàn)了一個(gè)簡單的string類。

string完整實(shí)現(xiàn)

構(gòu)造函數(shù),析構(gòu)函數(shù),拷貝構(gòu)造

之前我們實(shí)現(xiàn)的一個(gè)string類是一個(gè)最簡單的string類,它沒有辦法進(jìn)行增刪查改,接下來我們就來一點(diǎn)一點(diǎn)完善它。

要實(shí)現(xiàn)增刪查改,我們還需要兩個(gè)變量,一個(gè)記錄string類當(dāng)前長度,一個(gè)記錄string類的容量大小。加入這兩個(gè)變量后,我們原本的構(gòu)造函數(shù),拷貝構(gòu)造和析構(gòu)函數(shù)需要發(fā)生一點(diǎn)點(diǎn)變化。

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;
		}
	}
};

運(yùn)算符重載

接下來我們來實(shí)現(xiàn)一下,string類的運(yùn)算符。在實(shí)現(xiàn)運(yùn)算符重載時(shí),我們需要做的只是實(shí)現(xiàn)少數(shù)幾個(gè)運(yùn)算符即可,其他的運(yùn)算符可復(fù)用前面實(shí)現(xiàn)的運(yùn)算符來達(dá)到我們想要的效果。

//關(guān)系運(yùn)算符的重載
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);
}
//操作運(yùn)算符的重載
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接口實(shí)現(xiàn)

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

size_t size() const
{
    return _size;
}

size_t capacity() const
{
    return _capacity;
}

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

因?yàn)楹竺娴慕涌诖蟛糠侄夹枰M(jìn)行空間的調(diào)整,所以首先我們將調(diào)整空間的接口,reserve和resize實(shí)現(xiàn)。

void reserve(size_t n)
{
    if (n > _capacity) //判斷是否需要擴(kuò)容
    {
        char *tmp = new char[n + 1];
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}

//resize和reserve的區(qū)別在于,reserve只是開空間,而resize還要進(jìn)行初始化
void resize(size_t n, char c = '\0')
{
    if (n > _capacity)
    {
        reserve(n); //開空間復(fù)用reserve
    }
    for (size_t i = _size; i < n; ++i)
    {
        _str[i] = c;
    }
    _size = n;
    _str[_size] = '\0';
}

接下來是插入的實(shí)現(xiàn),首先是push_back,這個(gè)比較簡單,找到尾部進(jìn)行插入即可。

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

接下來是insert,這個(gè)較push_back而言要麻煩一些,因?yàn)槌宋膊澹渌胤饺ゲ迦霐?shù)據(jù)你都需要挪動(dòng)后面數(shù)據(jù)的位置。

string &insert(size_t pos, const char *str)
{
    //檢查空間是否足夠
    assert(pos <= _size);
    size_t len = strlen(str);
    if (len + _size > _capacity)
    {
        reserve(len + _size);
    }

   	//挪動(dòng)后面的數(shù)據(jù)
    size_t end = _size + len;
    while (end != pos + len - 1)
    {
        _str[end] = _str[end - len];
        --end;
    }

    //數(shù)據(jù)插入
    strncpy(_str + pos, str, len);
    _size += len;
    return *this;
}

寫完了插入,接下來當(dāng)然就是刪除接口:eraser

string &eraser(size_t pos, size_t len = npos) //npos為靜態(tài)變量,值為-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;
}

迭代器的實(shí)現(xiàn)

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

接下來我們來實(shí)現(xiàn)string的迭代器,其實(shí)string的迭代器就是一個(gè)指針。并不用去封裝特別的東西。

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;
}

部分函數(shù)優(yōu)化和完善

前面在寫運(yùn)算符重載時(shí),還有部分運(yùn)算符未重載在此加上

string &operator+=(const char *str)
{
    append(str);
}

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

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

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

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;
}

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

1.首先是拷貝構(gòu)造,我們在拷貝構(gòu)造中使用構(gòu)造函數(shù)去創(chuàng)建一個(gè)臨時(shí)對象,這個(gè)臨時(shí)對象在創(chuàng)建時(shí),就幫我們開辟了空間。然后我們將臨時(shí)對象和此對象的所有成員進(jìn)行一個(gè)交換,這樣此對象就可以接管臨時(shí)對象創(chuàng)建的那塊空間,我們的拷貝構(gòu)造也就成功了

2.在operator=這,我們使用的是傳值傳參。好處在于由于我們的string類是自定義對象,所以在傳參時(shí)會(huì)去調(diào)用拷貝構(gòu)造,這樣傳過來的str參數(shù)也擁有了自己的空間,此時(shí)我們和拷貝構(gòu)造一樣,將str所開辟的那塊空間接管,同時(shí)由于str是函數(shù)參數(shù),當(dāng)函數(shù)結(jié)束時(shí),str會(huì)去調(diào)用析構(gòu)函數(shù)進(jìn)行一個(gè)空間釋放。

完整代碼

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;

原文鏈接:https://blog.csdn.net/m0_60447315/article/details/126448202

欄目分類
最近更新