C++中的string类模拟实现

目录

string类的模拟实现

string类的构造函数

string类拷贝构造函数

string类析构函数

string类c_str()函数

string类中的[]运算符重载函数

string类中的赋值运算符重载

string类中获取字符串有效字符个数

string类中获取字符串存储空间大小(不包括\0)

string类reserve()函数

string类push_back()函数

string类append()函数

string类+=运算符重载函数

string类关系运算符重载

string类基础迭代器实现

string类resize()函数

string类insert()函数

string类erase()函数

string类find()函数

string类swap()函数

string类流插入运算符重载

string类流提取运算符重载

项目文件


string类的模拟实现

string类的构造函数

在设计string类的构造函数时,需要考虑到下面三个问题:

  1. 是否需要传递参数,即是否需要提供有参及无参构造函数
  2. 如何为字符串数组开辟空间,空间开辟的大小为多少
  3. 字符串数组的容量和有效数据个数之间的关系如何

针对上面三个问题,提出以下的解决方案:

  1. 在提供类构造函数时,需要为类提供两种构造函数:1. 有参构造 2. 无参构造。对于有参构造来说可以实现以类对象来构造或者以常量字符串来构造,而对于无参构造函数来说,可以不需要额外提供,只需要在以常量字符串构造的函数给一个空字符串""(空字符串不是没有内容,默认包含\0)作为缺省值即可
  2. 对于第二个问题,字符串数组开辟多大空间,对于无参构造函数来说,如果确保有缺省值可以防止出现空指针解引用的问题;而对于有参的构造函数来说,因为已经确定参数是个字符串,所以可以参数字符串的长度(不包括\0)作为初始大小以确保在开始时有足够空间
  3. 因为标准库中的capacity不计入\0占用的大小,所以本次模拟实现时capacity也不计入\0的个数,那么capacity即为字符串长度+1,但是第二个问题:因为使用的是缺省参数,所以无参就使用缺省值,而因为缺省值为1,如果在尾插时涉及到使用capacity进行扩容,那么会导致只能插入一个字符,后续字符将无法插入,所以综上capacity也需要给缺省值
//构造函数
string(const char* str = "", size_t capacity = 4)
    :_size(strlen(str))
{
    _capacity = (_size == 0 ? capacity : _size);
    //为字符串数组开辟空间
    _str = new char[_capacity + 1];
    strcpy(_str, str);
}

string类拷贝构造函数

虽然编译器默认会提供拷贝构造函数,但是该构造函数只能完成浅拷贝,如果对字符串数组进行浅拷贝,那么会出现两个问题:

  1. 两个指针指向同一个位置,当一个指针修改数组中的内容时,另一个指针指向的数组中的内容也会改变
  2. 当调用析构函数时,因为两个指针指向同一个位置,当一个指针被析构后,另一个指针并不会不析构,此时导致程序崩溃

所以为了避免上面的问题,string类的拷贝构造函数需要自行设计从而进行深拷贝

在设计string类的拷贝构造函数需要考虑到空间开辟的问题,因为是拷贝某一个对象中的内容,所以可以考虑使用该对象的capacity,但是注意需要+1,因为capacity不包括\0的占用空间

📌

注意拷贝构造函数的初始化列表为_size初始化

//拷贝构造函数
string(const string& s)
    :_size(s._size)//注意拷贝构造也需要初始化列表
    ,_capacity(s._capacity)
{
    _str = new char[s._capacity + 1];
    strcpy(_str, s._str);
}

string类析构函数

对于析构函数来说,只需要释放开辟的空间即可,因为使用的是new[]进行的空间开辟,所以需要使用delete[]进行空间释放

~string()
{
    delete[] _str;
    _size = _capacity = 0;
}

string类c_str()函数

因为C形式字符串是内置类型,所以使用cout打印时编译器会自动识别类型,相当于使用%s进行打印字符串,只需要返回字符串数组首字符地址即可

//返回C形式字符串
const char* c_str()
{
    return _str;//返回字符数组的首元素地址
}

string类中的[]运算符重载函数

对于下标引用操作符来说,只需要返回当前下标对应位置的字符即可,但是需要注意的是要判断传入的位置是否合法

📌

为了减少返回值拷贝到临时变量的消耗,推荐使用引用返回

const版本:

//重载[]
char& operator[](size_t pos)
{
    //确保pos不会越界
    assert(pos <= _size);
    return _str[pos];
}

const版本:

//重载[]——const
const char& operator[](size_t pos) const
{
    assert(pos <= _size);
    return _str[pos];
}

string类中的赋值运算符重载

虽然编译器默认会生成赋值运算符重载函数,但是对于内置类型和自定义类型都是浅拷贝,所以需要自行重载赋值运算符。

对于赋值运算符重载函数来说,需要考虑到空间是否足够的问题,有下面三种情况需要考虑:

  1. 源字符串和目标字符串所在空间大小基本一致

  1. 源字符串远大于目标字符串所在空间大小

  1. 源字符串远小于目标字符串所在空间大小

对于上面的三种情况来说,可以考虑的解决方式为:

  1. 对于第一种情况,使用大的一方的空间
  2. 对于第二种情况,扩容目标字符串空间,再将源字符串空间的内容赋值到目标字符串空间
  3. 对于第三种情况,释放目标字符串空间,再为目标字符串重新开辟空间,将源字符串空间的内容拷贝到目标字符串空间

但是,上面的解决方式过于复杂,所以考虑下面的思路:

不论是哪一种情况,先以目标字符串空间为基础开辟新的空间,再将源字符串中的内容直接拷贝到新的空间,这一步可以确保如果空间开辟失败不会影响源字符串中的内容,释放源字符串的空间,接着使源字符串的指针指向新的空间,

📌

注意处理自己给自己赋值的情况

//重载赋值运算符
string& operator=(const string& s)
{
    if (this != &s)
    {
        char* tmp = new char[s._capacity + 1];
        //此处new失败将会抛异常
        strcpy(tmp, s._str);
        delete[] _str;
        _str = tmp;
        _size = s._size;
        _capacity = s._capacity;
    }

    //最后不要忘记修改目标字符串的_size和_capacity
    return *this;
}

string类中获取字符串有效字符个数

对于_size来说,在函数外是不可以被修改的,所以返回const类型的变量,为了非const对象和const对象都可以调用该函数,用const修饰this指针

//返回字符串的有效字符个数
const size_t size() const
{
    return _size;
}

string类中获取字符串存储空间大小(不包括\0

对于_capacity来说,在函数外是不可以被修改的,所以返回const类型的变量,为了非const对象和const对象都可以调用该函数,用const修饰this指针

//返回字符串的容量大小
const size_t capacity() const
{
    return _capacity;
}

string类reserve()函数

对于扩容函数来说,只需要处理好原始空间的释放以及原始空间的内容不丢失即可

📌

注意,当扩容的大小小于原始大小时不能进行缩容

//扩容reserve()函数
void reserve(size_t capacity)
{
    //当扩容的容量小于_capacity时不进行缩容
    if (capacity <= _capacity)
    {
        return;
    }
    char* tmp = new char[capacity + 1];
    //new失败抛异常
    strcpy(tmp, _str);
    delete[] _str;
    _str = tmp;
    _capacity = capacity;
}

string类push_back()函数

对于push_back()函数来说,需要考虑的问题是插入数据时是否需要扩容

解决思路也很简单,因为直插入一个字符串,所以只需要将原来的容量+1即可

何时需要进行扩容

//字符串末尾追加字符
void push_back(char c)
{
    //判断是否需要扩容
    //因为_capacity是不包括\0的大小,如果_size+1>_capacity,那么意味着当前_size指向的位置即为\0的位置
    if (_size + 1 > _capacity)
    {
        reserve(_size + 1);//扩容一个字符即可,如果不想频繁扩容可以扩容_capacity*2
    }

    _str[_size++] = c;//在_size的位置插入字符c
    _str[_size] = '\0';
}

string类append()函数

对于append()函数的分析与push_back()函数相同,此处不再分析

//字符串末尾追加字符串
void append(const char* s)
{
    size_t len = strlen(s);
    //需要判断是否需要扩容
    if (_size + len > _capacity)
    {
        reserve(_size + len);
    }

    strcpy(_str + _size, s);
    _size += len;
}

string类+=运算符重载函数

//重载+=_字符串
string& operator+=(const char* s)
{
    //复用append()
    append(s);
    return *this;
}

//重载+=_字符
string& operator+=(const char c)
{
    //复用push_back()
    push_back(c);
    return *this;
}

string类关系运算符重载

底层直接调用strcmp()函数

//模拟实现strcmp
const int strcmp(const char* s1, const char* s2) const
{
    int i = 0;

    while (*s1 == *s2)
    {
        //如果两个都走到了\0的位置,那么说明二者相等
        if (!(*s1))
        {
            return 0;
        }
        s1++;
        s2++;
    }

    //走出循环后,说明当前两个指针指向的位置不同
    if (*s1 > *s2)
    {
        return 1;
    }
    else
    {
        return -1;
    }
}

//重载关系运算符
bool operator>(const string& s) const
{
    return strcmp(_str, s._str) > 0;
}

bool operator==(const string& s) const
{
    return strcmp(_str, s._str) == 0;
}

bool operator>=(const string& s) const
{
    //return *this > s || *this == s;
    return *this > s || s == *this;
}

bool operator<(const string& s) const
{
    return !(*this >= s);
}

bool operator<=(const string& s) const
{
    return !(*this > s);
}

bool operator!=(const string& s) const
{
    return !(*this == s);
}

string类基础迭代器实现

当前实现的迭代器是模拟指针的方式进行实现

//重载begin()_非const
iterator begin()
{
    return _str;
}

//重载end()_非const
iterator end()
{
    return _str + _size;//_size总是指向有效数据的下一个位置,一般为\0的位置
}

//重载begin()_const
const_iterator begin() const
{
    return _str;
}

//重载end()_const
const_iterator end() const
{
    return _str + _size;
}

string类resize()函数

在模拟实现resize()函数时,需要注意resize()不同于reserve()函数

reserve()函数只是对原有的字符串空间进行扩容,并且如果扩容的大小小于原始大小,那么将不执行扩容,而resize()函数需要分为下面三种情况:

  1. 当扩容的大小小于size时,将对原始字符串进行删除,直到只剩下给定大小个数的字符构成的字符串

  1. 当扩容的大小介于sizecapacity之间时,在原始字符串末尾进行初始化

  1. 当扩容大小大于capacity时,则需要对原始空间先进行扩容,再对扩充的空间进行初始化

//resize()函数
void resize(size_t size, char c = '\0')
{
    //当size小于等于_size时对于第一种情况
    if (size <= _size)
    {
        _str[size] = '\0';
        _size = size;
    }
    else
    {
        //需要扩容时对应第三种情况
        if (size > _capacity)
        {
            reserve(size);
        }

        size_t end = _size;
        while (end < size)
        {
            _str[end++] = c;
        }
        _str[size] = '\0';
    }
}

string类insert()函数

对于insert()函数来说有插入字符和插入字符串两种类型

首先对于插入字符来说,基本思路如下:

//insert()函数
void insert(size_t pos, const char c)
{
    assert(pos <= _size);
    //判断是否需要扩容

    if (_size + 1 > _capacity)
    {
        reserve(_capacity * 2);
    }

    //挪动pos位置之后的数据
    size_t end = _size;
    while (end + 1 >= pos)
    {
        _str[end + 1] = _str[end];
        end--;
    }
    _str[pos] = c;
    _size++;
}

但是需要注意一点,上面的方法在头部插入字符时会出现死循环以及越界访问(当前posend均为size_t类型),因为当end走到-1的位置时本应是最后一次循环,但是由于其为size_t类型,导致-1表示整型的最大值,从而造成end+1依旧大于pos,并且_str[end + 1] = _str[end]此时越界访问

可以考虑下面的修改方式

//insert()函数
void insert(size_t pos, const char c)
{
    assert(pos <= _size);
    //判断是否需要扩容

    if (_size + 1 > _capacity)
    {
        reserve(_capacity * 2);
    }

    //挪动pos位置之后的数据
    size_t end = _size;
    //当end为npos时跳出循环
    while (end + 1 >= pos && end != npos)
    {
        _str[end + 1] = _str[end];
        end--;
    }
    //单独在起始位置插入数据
    if (end == npos)
    {
        _str[0] = c;
    }
    _str[pos] = c;
    _size++;
}

也可以将上方代码优化成下面的代码,思路如下

//insert()函数_插入一个字符
void insert(size_t pos, const char c)
{
    assert(pos <= _size);
    //判断是否需要扩容

    if (_size + 1 > _capacity)
    {
        reserve(_capacity * 2);
    }

    //挪动pos位置之后的数据
    size_t end = _size + 1;
    while (end > pos)
    {
        _str[end] = _str[end - 1];
        end--;
    }

    _str[pos] = c;
    _size++;
}

插入字符串的思路也是一样,只是挪动数据的个数以及插入字符串的方式改变

//insert()函数_插入一个字符串
void insert(size_t pos, const char* s)
{
    assert(pos <= _size);
    //判断是否需要扩容
    size_t len = strlen(s);
    if (_size + len > _capacity)
    {
        reserve(_size + len);
    }

    //挪动pos位置之后的数据
    size_t end = _size + len;
    while (end > pos + len - 1)
    {
        _str[end] = _str[end - len];
        end--;
    }

    strncpy(_str + pos, s, len);
    _size += len;
}

string类erase()函数

erase()函数只是删除字符或者字符串的功能,注意需要分情况讨论:

  1. 当需要删除字符的个数小于字符串字符个数时,直接向前覆盖,再最后一个位置加入\0改变_size即可
  2. 当需要删除的字符个数大于字符串字符个数时,全部删除,此时只需要在pos位置加入\0改变_size即可
//erase()函数
void erase(size_t pos, size_t len = npos)
{
    assert(pos <= _size);

    //对应第二种情况
    if (len == npos || len + pos >= _size)
    {
        _str[pos] = '\0';
        _size = pos;
        return;
    }

    //对应第一种情况
    //size_t start = pos + len;
    //while (start <= _size)
    //{
    //    _str[pos++] = _str[start++];
    //}
    strcpy(_str + pos, _str + pos + len);
    _size -= len;
}

string类find()函数

find()函数也有两种类型,一种是找字符,另一种是找字符串

首先是找字符,直接挨个比较即可

//find()函数_找字符
size_t find(const char c, size_t pos = 0)
{
    assert(pos <= _size);
    for (size_t i = 0; i < _size; i++)
    {    
        if (_str[i] == c)
        {
            return i;
        }
    }
    return npos;
}

接着是找字符串,调用strstr()函数即可

//find()函数_找字符串
size_t find(const char* s, size_t pos = 0)
{
    char* ptr = strstr(_str, s);
    if (ptr == NULL)
    {
        return npos;
    }
    return ptr - _str;//两个指针相减返回差值
}

string类swap()函数

void swap(string& s)
{
    std::swap(_str, s._str);
    std::swap(_size, s._size);
    std::swap(_capacity, s._capacity);
}

string类流插入运算符重载

//流插入运算符重载
ostream& operator<<(ostream& cout, string& s)
{
    for (auto ch : s)
    {
        cout << ch;
    }
    return cout;
}

string类流提取运算符重载

//流提取运算符重载
istream& operator>>(istream& cin, string& s)
{
    s.clear();
    char ch = cin.get();
    char _buf[128] = {0};
    size_t i = 0;
    while (ch != ' ' && ch != '\n')
    {
        _buf[i++] = ch;
        if (i == 127)
        {
            s += _buf;
            i = 0;
        }
        ch = cin.get();
    }

    if (i != 0)
    {
        _buf[i] = '\0';
        s += _buf;
    }

    return cin;
}

项目文件

//头文件
#pragma once

#include <iostream>
#include <cassert>
using namespace std;

namespace sim_string
{
    class string
    {
    private:
        char* _str;//指向存储字符串的空间
        size_t _capacity;
        size_t _size;

    public:
        const static size_t npos = - 1;
        //构造函数
        string(const char* str = "", size_t capacity = 4)
            :_size(strlen(str))
        {
            _capacity = (_size == 0 ? capacity : _size);
            //为字符串数组开辟空间
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        //拷贝构造函数
        string(const string& s)
            :_size(s._size)//注意拷贝构造也需要初始化列表
            , _capacity(s._capacity)
        {
            _str = new char[s._capacity + 1];
            strcpy(_str, s._str);
        }

        ~string()
        {
            delete[] _str;
            _size = _capacity = 0;
        }

        //重载[]——非const
        char& operator[](size_t pos)
        {
            //确保pos不会越界
            assert(pos <= _size);
            return _str[pos];
        }

        //重载[]——const
        const char& operator[](size_t pos) const
        {
            assert(pos <= _size);
            return _str[pos];
        }

        //重载赋值运算符
        string& operator=(const string& s)
        {
            if (this != &s)
            {
                char* tmp = new char[s._capacity + 1];
                //此处new失败将会抛异常
                strcpy(tmp, s._str);
                delete[] _str;
                _str = tmp;
                _size = s._size;
                _capacity = s._capacity;
            }

            //最后不要忘记修改目标字符串的_size和_capacity
            return *this;
        }

        //返回C形式字符串
        const char* c_str()
        {
            return _str;//返回字符数组的首元素地址
        }

        //返回字符串的有效字符个数
        const size_t size() const
        {
            return _size;
        }

        //返回字符串的容量大小
        const size_t capacity() const
        {
            return _capacity;
        }

        //扩容reserve()函数
        void reserve(size_t capacity)
        {
            //当扩容的容量小于_capacity时不进行缩容
            if (capacity <= _capacity)
            {
                return;
            }
            char* tmp = new char[capacity + 1];
            //new失败抛异常
            strcpy(tmp, _str);
            delete[] _str;
            _str = tmp;
            _capacity = capacity;
        }
        //字符串末尾追加字符
        void push_back(const char c)
        {
            //判断是否需要扩容
            //因为_capacity是不包括\0的大小,如果_size+1>_capacity,那么意味着当前_size指向的位置即为\0的位置
            if (_size + 1 > _capacity)
            {
                reserve(_size + 1);//扩容一个字符即可,如果不想频繁扩容可以扩容_capacity*2
            }

            _str[_size++] = c;//在_size的位置插入字符c
            _str[_size] = '\0';
        }

        //字符串末尾追加字符串
        void append(const char* s)
        {
            size_t len = strlen(s);
            //需要判断是否需要扩容
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }

            strcpy(_str + _size, s);
            _size += len;
        }

        //重载+=_字符串
        string& operator+=(const char* s)
        {
            //复用append()
            append(s);
            return *this;
        }

        //重载+=_字符
        string& operator+=(const char c)
        {
            //复用push_back()
            push_back(c);
            return *this;
        }

        //模拟实现strcmp
        const int strcmp(const char* s1, const char* s2) const
        {
            int i = 0;

            while (*s1 == *s2)
            {
                //如果两个都走到了\0的位置,那么说明二者相等
                if (!(*s1))
                {
                    return 0;
                }
                s1++;
                s2++;
            }

            //走出循环后,说明当前两个指针指向的位置不同
            if (*s1 > *s2)
            {
                return 1;
            }
            else
            {
                return -1;
            }
        }

        //重载关系运算符
        bool operator>(const string& s) const
        {
            return strcmp(_str, s._str) > 0;
        }

        bool operator==(const string& s) const
        {
            return strcmp(_str, s._str) == 0;
        }

        bool operator>=(const string& s) const
        {
            //return *this > s || *this == s;
            return *this > s || s == *this;
        }

        bool operator<(const string& s) const
        {
            return !(*this >= s);
        }

        bool operator<=(const string& s) const
        {
            return !(*this > s);
        }

        bool operator!=(const string& s) const
        {
            return !(*this == s);
        }

        typedef char* iterator;
        typedef const char* const_iterator;

        //重载begin()_非const
        iterator begin()
        {
            return _str;
        }

        //重载end()_非const
        iterator end()
        {
            return _str + _size;//_size总是指向有效数据的下一个位置,一般为\0的位置
        }

        //重载begin()_const
        const_iterator begin() const
        {
            return _str;
        }

        //重载end()_const
        const_iterator end() const
        {
            return _str + _size;
        }

        //insert()函数_插入一个字符
        void insert(size_t pos, const char c)
        {
            assert(pos <= _size);
            //判断是否需要扩容

            if (_size + 1 > _capacity)
            {
                reserve(_capacity * 2);
            }

            //挪动pos位置之后的数据
            size_t end = _size + 1;
            while (end > pos)
            {
                _str[end] = _str[end - 1];
                end--;
            }

            _str[pos] = c;
            _size++;
        }

        //insert()函数_插入一个字符串
        void insert(size_t pos, const char* s)
        {
            assert(pos <= _size);
            //判断是否需要扩容
            size_t len = strlen(s);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }

            //挪动pos位置之后的数据
            size_t end = _size + len;
            while (end > pos + len - 1)
            {
                _str[end] = _str[end - len];
                end--;
            }

            strncpy(_str + pos, s, len);
            _size += len;
        }

        //void insert(size_t pos, const char c)
        //{
        //    assert(pos <= _size);
        //    //判断是否需要扩容
        //    if (_size + 1 > _capacity)
        //    {
        //        reserve(_capacity * 2);
        //    }
        //    //挪动pos位置之后的数据
        //    size_t end = _size;
        //    while (end + 1 >= pos && end != npos)
        //    {
        //        _str[end + 1] = _str[end];
        //        end--;
        //    }
        //    if (end == npos)
        //    {
        //        _str[0] = c;
        //    }
        //    _str[pos] = c;
        //    _size++;
        //}

        //resize()函数
        void resize(size_t size, char c = '\0')
        {
            //当size小于等于_size时对于第一种情况
            if (size <= _size)
            {
                _str[size] = '\0';
                _size = size;
            }
            else
            {
                //需要扩容时对应第三种情况
                if (size > _capacity)
                {
                    reserve(size);
                }

                size_t end = _size;
                while (end < size)
                {
                    _str[end++] = c;
                }
                _str[size] = '\0';
            }
        }

        //erase()函数
        void erase(size_t pos, size_t len = npos)
        {
            assert(pos <= _size);

            //对应第二种情况
            if (len == npos || len + pos >= _size)
            {
                _str[pos] = '\0';
                _size = pos;
                return;
            }

            //对应第一种情况
            //size_t start = pos + len;
            //while (start <= _size)
            //{
            //    _str[pos++] = _str[start++];
            //}
            strcpy(_str + pos, _str + pos + len);
            _size -= len;
        }

        //find()函数_找字符
        size_t find(const char c, size_t pos = 0)
        {
            assert(pos <= _size);
            for (size_t i = 0; i < _size; i++)
            {    
                if (_str[i] == c)
                {
                    return i;
                }
            }
            return npos;
        }

        //find()函数_找字符串
        size_t find(const char* s, size_t pos = 0)
        {
            char* ptr = strstr(_str, s);
            if (ptr == NULL)
            {
                return npos;
            }
            return ptr - _str;//两个指针相减返回差值
        }

        void swap(string& s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }

        //clear()函数
        void clear()
        {
            _str[0] = '\0';
            _size = 0;
        }
    };
    //流插入运算符重载
    ostream& operator<<(ostream& cout, string& s)
    {
        for (auto ch : s)
        {
            cout << ch;
        }
        return cout;
    }
    //流提取运算符重载
    istream& operator>>(istream& cin, string& s)
    {
        s.clear();
        char ch = cin.get();
        char _buf[128] = {0};
        size_t i = 0;
        while (ch != ' ' && ch != '\n')
        {
            _buf[i++] = ch;
            if (i == 127)
            {
                s += _buf;
                i = 0;
            }
            ch = cin.get();
        }

        if (i != 0)
        {
            _buf[i] = '\0';
            s += _buf;
        }

        return cin;
    }
}

//测试文件
#define _CRT_SECURE_NO_WARNINGS 1

#include "simulat_string.h"

void test_structure()
{
    sim_string::string s1;
    sim_string::string s2("hello world");
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
}

void test_copyStructure()
{
    sim_string::string s1("hello world");
    sim_string::string s2(s1);
    cout << s2.c_str() << endl;
    //s1[6]++;
    //cout << s1.c_str() << endl;
    //cout << s2.c_str() << endl;
}

void test_operator_pos()
{
    sim_string::string s1("hello world");
    for (size_t i = 0; i < s1.size(); i++)
    {

        cout << s1[i] << " ";
    }
    cout << endl;
    //可以通过下标进行修改
    for (size_t i = 0; i < s1.size(); i++)
    {
        s1[i]++;
        cout << s1[i] << " ";
    }
    cout << endl;
    const sim_string::string s2("hello Linux");
    for (size_t i = 0; i < s2.size(); i++)
    {
        //s2[i]++;不可以修改
        cout << s2[i] << " ";
    }
}

void test_operator_give()
{
    //当目标对象大于源对象
    sim_string::string s1("xxxx");
    sim_string::string s2 = "hello world";//注意此处不是赋值运算符重载,而是没有explicit修饰的构造函数
    s1 = s2;
    cout << s1.c_str() << endl;

    //当目标对象小于源对象
    sim_string::string s3("xxxxxxxxxxxxxxxxxxxxx");
    sim_string::string s4 = "hello";
    s3 = s4;
    cout << s3.c_str() << endl;
    
    //当目标对象等于源对象
    sim_string::string s5 = "xxxxxx";
    sim_string::string s6 = "------";
    s5 = s6;
    cout << s5.c_str() << endl;
}

void test_push_back()
{
    sim_string::string s1 = "hello world";
    s1.push_back('d');
    cout << s1.c_str() << endl;

    //sim_string::string s2;
    //s2.push_back('a');
    //s2.push_back('b');
    //s2.push_back('c');
    //cout << s2.c_str() << endl;
}

void test_append()
{
    sim_string::string s1 = "hello world";
    s1.append(" Linux");

    cout << s1.c_str() << endl;
}

void test_operator_addEqual()
{
    sim_string::string s1 = "hello world";

    s1 += 'c';
    cout << s1.c_str() << endl;

    s1 += " Linux";
    cout << s1.c_str() << endl;
}

void test_relational_operators()
{
    sim_string::string s1 = "abcde";
    sim_string::string s2 = "abcdef";
    cout << (s1 < s2) << endl;
    sim_string::string s3 = "abef";
    sim_string::string s4 = "abcd";
    cout << (s3 > s4) << endl;
    sim_string::string s5 = "abc";
    sim_string::string s6 = "abc";
    cout << (s5 != s6) << endl;
}


void test(const sim_string::string& s1)
{
    //sim_string::string::const_iterator ptr1 = s1.begin();
    //while (ptr1 != s2.end())
    //{
    //    cout << *ptr1 << " ";
    //    ptr1++;
    //}

    //const版本可以使用范围for,但是不可以修改值内容
    for (auto ch : s1)
    {
        cout << ch << " ";
    }
}

void test_iterator()
{
    //sim_string::string s1 = "hello world";
    //sim_string::string::iterator ptr = s1.begin();
    //while (ptr != s1.end())
    //{
    //    cout << *ptr << " ";
    //    ptr++;
    //}

    //非const版本可以使用范围for并且修改字符串数组的内容
    //for (auto ch : s1)
    //{
    //    ch++;
    //    cout << ch << " ";
    //}

    const sim_string::string s2 = "hello world";
    test(s2);
}

void test_insert()
{
    //sim_string::string s1 = "hello world";
    //s1.insert(0, 'd');
    //cout << s1.c_str() << endl;

    sim_string::string s2 = "hello";
    s2.insert(2, "world");
    cout << s2.c_str() << endl;
    s2.insert(0, "Linux");
    cout << s2.c_str() << endl;
}

void test_resize()
{
    sim_string::string s2 = "hello world";
    s2.resize(20, 'd');
    cout << s2.c_str() << endl;
    s2.resize(15, 'c');
    cout << s2.c_str() << endl;
    s2.resize(5);
    cout << s2.c_str() << endl;
}

void test_ostream_operator()
{
    sim_string::string s1 = "hello world";
    cout << s1;
}

void test_istream_operator()
{
    sim_string::string s2;
    cin >> s2;
    cout << s2;
    sim_string::string s3("hello world");
    cin >> s3;
    cout << s3;
}

void test_erase()
{
    sim_string::string s1 = "hello world";
    s1.erase(5, 5);
    cout << s1.c_str() << endl;
    s1.erase(1);
    cout << s1.c_str() << endl;
}

void test_find()
{
    sim_string::string s1 = "hello world";
    size_t pos = s1.find('g');
    //cout << pos << endl;
    if (pos != sim_string::string::npos)
    {
        cout << pos << endl;
    }
    else
    {
        cout << "无" << endl;
    }

    pos = s1.find("wol");
    //cout << pos << endl;

    if (pos != sim_string::string::npos)
    {
        cout << pos << endl;
    }
    else
    {
        cout << "无" << endl;
    }
}

void test_swap()
{

    sim_string::string s1 = "hello world";
    sim_string::string s2 = "hello Linux";
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
    s1.swap(s2);
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
}

int main()
{
    //test_structure();
    //test_copyStructure();
    //test_operator_pos();
    //test_operator_give();
    //test_push_back();
    //test_append();
    //test_operator_addEqual();
    //test_relational_operators();
    //test_iterator();
    //test_insert();
    //test_resize();
    //test_erase();
    //test_find();
    //test_swap();
    //test_ostream_operator();
    test_istream_operator();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/513417.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

把标注数据导入到知识图谱

文章目录 简介数据导入Doccano标注数据&#xff0c;导入到Neo4j寻求帮助 简介 团队成员使用 Doccano 标注了一些数据&#xff0c;包括 命名实体识别、关系和文本分类 的标注的数据&#xff1b; 工作步骤如下&#xff1a; 首先将标注数据导入到Doccano&#xff0c;查看一下标注…

配置vite配置文件更改项目端口、使用@别名

一、配置vite配置文件更改项目端口 vite官方文档地址&#xff1a;开发服务器选项 | Vite 官方中文文档 (vitejs.dev) 使用&#xff1a; 二、使用别名 1. 安装 types/node types/node 包允许您在TypeScript项目中使用Node.js的核心模块和API&#xff0c;并提供了对它们的类型…

嵌入式面试八股文

大家好&#xff0c;今天给大家介绍嵌入式面试八股文&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 嵌入式面试中&#xff0c;常被问到的问题通常涉及嵌入式系统的基础知识、应用…

zabbix源码安装

目录 一.安装php和nginx客户端环境 二.修改php配置 三.修改nginx配置文件 四.下载并编译zabbix 五.创建zabbix需要的用户及组 六.安装编译需要的依赖 七.配置zabbix文件 八.数据库配置 九.配置zabbix 十.web界面部署 十一.遇到无法创建配置文件 十二.登录zabbix 前…

非关系型数据库-----------Redis的主从复制、哨兵模式

目录 一、redis群集有三种模式 1.1主从复制、哨兵、集群的区别 1.1.1主从复制 1.1.2哨兵 1.1.3集群 二、主从复制 2.1主从复制概述 2.2主从复制的作用 ①数据冗余 ②故障恢复 ③负载均衡 ④高可用基石 2.3主从复制流程 2.4搭建redis主从复制 2.4.1环境准备 2.4…

Sy6 编辑器vi的应用(+shell脚本3例子)

实验环境&#xff1a; 宿主机为win11&#xff0c;网络&#xff1a;10.255.50.5 6389 WSL2 ubuntu 目标机的OS&#xff1a;Ubuntu 内核、版本如下&#xff1a; linuxpeggy0223:/$ uname -r 5.15.146.1-microsoft-standard-WSL2 linuxpeggy0223:/$ cat /proc/version Linux vers…

解析二极管的单向导电性

二极管是电子电路中很常用的元器件&#xff0c;非常常见&#xff0c;二极管具有正向导通&#xff0c;反向截止的特性。 在二极管的正向端&#xff08;正极&#xff09;加正电压&#xff0c;负向端&#xff08;负极&#xff09;加负电压&#xff0c;二极管导通&#xff0c;有电流…

云计算的安全需求

目录 一、概述 二、云安全服务基本能力要求 三、信息安全服务&#xff08;云计算安全类&#xff09;资质要求 3.1 概述 3.2 资质要求内容 3.2.1 组织与管理要求 3.2.2 技术能力要求 四、云安全主要合规要求 4.1 安全管理机构部门的建立 4.2 安全管理规范计划的编制 4…

计算机视觉之三维重建(5)---双目立体视觉

文章目录 一、平行视图1.1 示意图1.2 平行视图的基础矩阵1.3 平行视图的极几何1.4 平行视图的三角测量 二、图像校正三、对应点问题3.1 相关匹配法3.2 归一化相关匹配法3.3 窗口问题3.4 相关法存在的问题3.5 约束问题 一、平行视图 1.1 示意图 如下图即是一个平行视图。特点&a…

大数据设计为何要分层,行业常规设计会有几层数据

大数据设计通常采用分层结构的原因是为了提高数据管理的效率、降低系统复杂度、增强数据质量和可维护性。这种分层结构能够将数据按照不同的处理和应用需求进行分类和管理&#xff0c;从而更好地满足不同层次的数据处理和分析需求。行业常规设计中&#xff0c;数据通常按照以下…

java框架学习——注解/元注解概述及使用案例

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! 注解 注解&#xff08;Annotation&#xff09;是java代码里的特殊标记。作用为&#xff1a;让其他程序根据注解信息来决定怎么执行该程序&#xff0c;如&#xff1a;Override,Test等。同时可以根…

安装pillow库的方法最终解答!

安装pillow库&#xff0c;记录留痕。 Pillow库是一个非常强大的图像处理库。它提供了广泛的图像处理功能&#xff0c;让我们可以轻松地操作图像&#xff0c;实现图像的转换、裁剪、缩放、旋转等操作。此外&#xff0c;Pillow还支持多种图像格式的读取和保存&#xff0c;包括JP…

「每日跟读」句型公式 第2篇

「每日跟读」句型公式 第2篇 1. I’m thinking about____ 我在考虑____ I’m thinking about my future career (我正在思考我未来的职业) I’m thinking about our marriage (我在考虑我们的婚姻) I’m thinking about taking a vacation (我在考虑度一个假) I’m think…

【RedHat9.0】Timer定时器——创建单调定时器实例

一个timer&#xff08;定时器&#xff09;的单元类型&#xff0c;用来定时触发用户定义的操作。要使用timer的定时器&#xff0c;关键是要创建一个定时器单元文件和一个配套的服务单元文件&#xff0c;然后启动这些单元文件。 定时器类型&#xff1a; 单调定时器&#xff1a;即…

回溯算法 DFS

目录 回溯算法和dfs的区别回溯算法基本框架例题&#xff1a;【1,2&#xff0c;3】的全排列代码详解完整代码 DFS 本文思路、代码均参考于&#xff1a;https://labuladong.online/algo/essential-technique/backtrack-framework-2/#%E4%B8%80%E3%80%81%E5%85%A8%E6%8E%92%E5%88%…

【数字图像处理】图像的最近邻插值、双线性插值和双三次插值

图像最近邻插值、双线性插值和双三次插值 用 O ( X , Y ) O(X,Y) O(X,Y)表示 H W H\times W HW的原始图像&#xff0c; G ( X ^ , Y ^ ) G(\hat{X},\hat{Y}) G(X^,Y^)表示 H ^ Y ^ \hat{H}\times\hat{Y} H^Y^的目标图像。 最近邻插值 最近邻插值法令目标图像在 ( x ^ , y…

深入理解直播美颜SDK背后的深度学习原理

直播美颜SDK技术背后涉及了深度学习原理的应用&#xff0c;今天我将为大家讲解美颜SDK其中的深度学习算法&#xff0c;还有一些基本原理与关键技术。 一、深度学习在直播美颜中的应用 直播美颜SDK的核心是基于深度学习的算法模型。这些模型通常由多个卷积神经网络组成&#xf…

SCI一区 | Matlab实现BES-TCN-BiGRU-Attention秃鹰算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现BES-TCN-BiGRU-Attention秃鹰算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现BES-TCN-BiGRU-Attention秃鹰算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述程序…

VS2022配置boost库-Windows为例

1. boost库下载 1&#xff09;下载boost库源码&#xff1a;https://www.boost.org/ 2&#xff09;以1.81版本为例&#xff0c;安装包如下 3&#xff09;下载后解压 比如我是放在E盘下面的boost文件夹 2. 安装配置 1&#xff09;打开VS2022命令行 2&#xff09;切换安装…

智慧城市治理:构建全域覆盖的城市时空感知体系

TSINGSEE青犀AI算法中台是一款平台型产品&#xff0c;专注于提供各行业中小场景部署解决方案。平台具备接入广、性能强、支持跨平台、芯片国产化等特点&#xff0c;可提供丰富的视图接入能力和智能分析能力。 平台采用了多项IT高新技术&#xff0c;包括视频编解码技术、嵌入式…