【C++】string类的模拟实现(中)

简介: 【C++】string类的模拟实现(中)

4. 析构函数

析构函数就非常简单了,使用delete[]释放空间即可

~st


3.迭代器


对于string的迭代器,原生指针就能很好的支持迭代器行为,所以我们直接用原生指针。这里我们只实现正向迭代器的const和非const版本。

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


4.容量相关


1.size和capacity

size和capacity直接返回对象中的成员变量即可

size_t size()
{
    return _size;
}
size_t capacity()
{
    return _capacity;
}


2. reserve

reserve是对string对象的容量进行操控的,当容量小于传入的值时,将会扩容,如果容量大于传入的值将不会做任何操作

{
    if (n > _capacity)
    {
        char* tmp = new char[n + 1];
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}


3. resize

resize的功能就是对string对象的size重新规划,当传入的n > capacity时,需要扩容,然后使用‘\0’来填充(默认情况下),当n小于元素个数时,将会把元素个数直接变成n,当n在size和capacity之间时,将会把后续的内容填充能‘\0’(默认情况下)

void reseize(size_t n, char ch = '\0')
{
    if (n > _capacity)
    {
        reserve(n);
    }
    if (n > _size)
    {
        for (size_t i = _size; i < n; ++i)
        {
            _str[i] = ch;
        }
    }
    _size = n;
    _str[n] = '\0';

4. clear和empty

clear是清除所有数据,但是不销毁的函数,empty是判断是否为空的函数

void clear()
{
    _size = 0;
    _str[0] = '\0';
}
bool empty()
{
    return _size == 0;
}


5.数据访问


对于string的数据访问,我们一般有两种方式,第一就是通过迭代器(范围for也是迭代器的方式),第二种就是通过operator[],所以这里我们只实现一下operator[]

//非const版本,可读可写
char operator[](size_t pos)
{
    assert(pos < _size);
    return _str[pos];
}
//const版本,可读不可写
const char operator[](size_t pos) const
{
    assert(pos < _size);
    return _str[pos];
}


6.数据操作


在学习数据结构的时候,我们一般学习的就是这种数据结构的增删查改四种操作,对于string类也是这样,所以在数据操作中,也分为增删查改四种。


1.插入数据

1. push_back

尾插一个字符,在增加数据的时候,我们需要考虑是否需要扩容

void push_back(char ch)
{
    if (_capacity == _size)
    {
        size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;//扩容扩二倍
        reserve(newCapacity);
    }
    _str[_size] = ch;
    ++_size;
    _str[_size] = '\0';//这里注意尾插之后要对字符串结尾的\0做一下补充
}


2. append

尾插一串字符(一个字符串),这里有很多重载,我们只实现一个C-string类型的。

void append(const char* str)
{
    //这里需要判断一下需不需要扩容,如果需要的话要扩多大
    size_t len = strlen(str);
    if (_size + len > _capacity)
    {
        reserve(_size + len);
    }
    strcpy(_str + _size, str);
    _size += len;
}


3. operator+=

对于上述的两种插入方式,我们可以使用一个运算符重载全部解决,直接复用即可

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


4.insert

1. 插入字符

按照insert的逻辑,我们很容易的可以写出以下代码

string insert(size_t pos, char ch)
{
    //判断位置是否合法
    assert(pos <= _size);
    //扩容
    if (_size == _capacity)
    {
        size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;
        reserve(newCapacity);
    }
    //挪动数据
    size_t end = _size;
    while (pos <= end)
    {
        _str[end + 1] = _str[end];
        --end;
    }
    _str[pos] = ch;
    ++_size;
    return *this;
}


但是,如果当传入的pos==0时,将会出现死循环的问题,因为pos的类型是size_t,所以在进行比较的时候,编译器会进行隐式类型转换,把end转换成size_t的类型进行比较,所以如果传入的pos==0,就不可能出现pos > end的情况,也就是死循环了,那么我们要怎么解决这个问题呢?其实有两种解决方案


  • 方案一:强转

将end定义成int类型,然后while中的判断把pos强转成int,这样就可以避免隐式类型转换,从而规避死循环的情况发生

int end = _size;
while ((int)pos <= end)
{
    _str[end + 1] = _str[end];
    --end;
}


但是这种方法看起来有点不太高级,所以这里我们有了另一种方法


  • 方案二:将end指向\0后面的位置,这样就不会出现end<0的情况
size_t end = _size + 1;
while (pos < end)
{
    _str[end] = _str[end - 1];
    --end;
}


2. 插入字符串

插入字符串之前,我们同样要判断容量是否充足。这里在拷贝的过程中,我们要注意使用strcpy的时候,会把\0拷贝进去,所以我们不能使用strcpy而是使用strncpy。

string& insert(size_t pos, const char* str)
{
    //判断位置是否合法
    assert(pos <= _size);
    size_t len = strlen(str);
    //扩容
    if (_capacity < len + _size)
    {
        reserve(len + _size);
    }
    //挪动数据
    size_t end = _size + pos;
    while (pos + len - 1 < end)
    {
        _str[end] = _str[end - len];
        --end;
    }
    strncpy(_str + pos, str, len);
    _size += len;
    return *this;
}


相关文章
|
1天前
|
编译器 C++
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
|
1天前
|
安全 算法 C语言
【C++进阶】深入STL之string:掌握高效字符串处理的关键
【C++进阶】深入STL之string:掌握高效字符串处理的关键
【C++进阶】深入STL之string:掌握高效字符串处理的关键
|
1天前
|
编译器 C++
【C++初阶】—— 类和对象 (下)
【C++初阶】—— 类和对象 (下)
|
1天前
|
存储 编译器 C++
【C++初阶】—— 类和对象 (中)
【C++初阶】—— 类和对象 (中)
|
1天前
|
存储 编译器 C语言
【C++初阶】—— 类和对象 (上)
【C++初阶】—— 类和对象 (上)
|
2天前
|
程序员 C语言 C++
【C++语言】继承:类特性的扩展,重要的类复用!
【C++语言】继承:类特性的扩展,重要的类复用!
|
2天前
|
C++ 容器
【C++语言】String 类关键函数实现,手搓一个简单字符串类!
【C++语言】String 类关键函数实现,手搓一个简单字符串类!
|
2天前
|
C++ 容器 存储
【C++语言】想学STL,先细细拿捏string类,万字详解string类 (内附精美思维导图)
【C++语言】想学STL,先细细拿捏string类,万字详解string类 (内附精美思维导图)
|
2天前
|
C++ 编译器
【C++语言】Date类的代码实现(操作符重载运用)
【C++语言】Date类的代码实现(操作符重载运用)
|
2天前
|
C++
【C++语言】类和对象(下)
【C++语言】类和对象(下)