• 【C++】string模拟实现


    💒sting

    🧸练习

    💜写一个简单的string

    namespace luo
    {
    	class string
    	{
    	public:
    		//构造函数
    		string(const char* str)
    			:_str(new char[strlen(str)+1])
    		{
    			strcpy(_str, str);
    		}
    		//析构函数
    		~string()
    		{
    			delete[] _str;
    			_str = nullptr;
    		}
    	private:
    		char* _str;
    	};
    	void test_string1()
    	{
    		string s1("hello world");
    		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    💜构造函数

    🌸s1(“hello”)

    浅拷贝(s1、s2指向同一块空间)

    当对象出作用域后,同一块空间将析构两次,则会出现错误

    //s1("hello")
    string(const char* str)
        :_str(new char[strlen(str)+1])//开空间
    {
        strcpy(_str, str);//按字符拷贝,包括\0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🌸strcpy()

    char * strcpy ( char * 目标, const char * 源);
    将源指向的C字符串复制到目标指向的数组中,包括终止的字符(并在该点停止);
    
    • 1
    • 2

    🌸s1(s2)

    深拷贝:开空间后在拷贝

    //s2(s1)
    string(const string& s)
        :_str(new char[strlen(s._str)+1])
    {
        strcpy(_str, s._str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🌸s3 = s1

    //s3 = s1
    string& operator=(const string& s)
    {
        //避免自己赋值自己 判断一下
        if (this != &s)
        {
            char* tmp = new char[strlen(s._str) + 1];
            strcpy(tmp,s._str);
            delete[] _str;
            _str = tmp;
        }
        return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    💜问题:

    浅拷贝造成的 问题如何解决?

    先开好自己的空间 ,再strcpy数据。

    🧸string类实现

    private:
        char* _str;
        size_t _size;//存储有效字符
        size_t _capacity;//显示能存储的有效空间,排除\0
    
    • 1
    • 2
    • 3
    • 4

    💜构造函数

    🌸string(const char* str)

    //构造函数
    // "\0"的效果相同 这里不能给nullptr ""常量字符串默认添加\0
    // 因为strlen 直接解引用访问就会 造成空指针
    string(const char* str = "")//带缺省值如果是空str 则用缺省值,""里是\0
        :_size(strlen(str))//strlen("\0") == 0
        ,_capacity(_size)//拷贝构造,str多大,capacity就多大
    {
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    🌸string(const string& s)

    //s2(s1) sting参数
    string(const string& s)
        :_size(s._size)//s2.size = s1.size
        ,_capacity(s._capacity)//s2.capacity = s1.capacity
    {
        _str = new char[strlen(s._str) + 1];//开空间,多开一个为\0准备
        strcpy(_str, s._str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    复用string(const char* str)

    string(const string &s)
    	:_str(nullptr)
    	,_size(0)
    	,_capacity(0)
    {
    	string tmp(s._str);//复用string(const char* str)
    	//this->swap(tmp);
    	swap(tmp);//swap在下面
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    💜析构函数

    🌸~string()

    ~string()
    {
    	delete[] _str;
    	_str = nullptr;
    	_size = _capacity = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    💜迭代器

    🌸begin()

    是char*类型,指向string的第一个字符

    迭代器返回的是 迭代器(iterator)类型

    typedef char*iterator;
    typedef const char*const_iterator;
    const_iterator begin() const
    {
    	return _str;
    }
    iterator begin() 
    {
    	return _str;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    🌸end()

    是char*类型,指向string的最后一个字符的下一个位置

    typedef char*iterator;
    typedef const char*const_iterator;
    const_iterator end() const
    {
    	return _str + _size;
    }
    iterator end()
    {
        return _str + _size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    💜成员函数

    🌸clear()

    //清理数据/不清理空间

    void clear()
    {
        _str[0] = '\0';
        _size = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    🌸swap()

    交换函数

    void swap(string &s)
    {
    	std::swap(_str,s._str);//指定std里的swap
    	std::swap(_size,s._size);
    	std::swap(_capacity,s._capacity);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    🌸c_str()

    char*类型 返回c类型字符串,包含\0

    因为_str是私有的,访问不到,所以有了c_str();又因为这个字符串不支持修改,所以返回const类型 ,而后面的const修饰的是this指针,this指针内容不能被修改

    //c_str 遇到\0结束
    const char* c_str() const
    {
    	return _str;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    🌸size()

    size_t类型,返回的是string类型创建的对象,的有效字符,不包含\0

    size_t size() cosnt
    {
    	return _size;
    }
    
    • 1
    • 2
    • 3
    • 4

    🌸operator[]:访问函数

    返回的是 char&, 引用返回,则首先可以减少拷贝,其次可以修改返回的参数

    char& operator[](size_t pos)
    {
    	assert(pos<_size);
    	return _str[pos];
    }
    const char& operator[](size_t pos) const
    {
    	assert(pos<_size);
    	return _str[pos];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    🌸reverse():扩容

    只扩容,不初始化,当n<capacity,不会缩容

    void reverse(size_t n)
    {
    	//判断一下,只增容,不缩容
    	if(n>_capacity)
    	{
    		char* tmp = new char[n+1];//有效字符+\0
    		strcpy(tmp ,_str);
    		delete[] _str;
    		_str = tmp;
    		_capacity = n;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    🌸resize():扩容+初始化

    扩容的同时 默认将扩容的空间初始化为0;

    可以给定值初始化

    如果n<capacity,不会缩容,但是会将有效字符size 减少到n个

    void resize(size_t n,char ch = '\0')
    {
    	if(n < _size)
    	{
    		_size = n;
    		_str[_size] = '\0';
    	}
    	else
    	{
    		if(n > _capacity)
    		{
    			reverse(n);
    		}
    		memset(_str + _size,ch,n - _size);//起始位置,字符,拷贝多少个字符
    		_size = n;
    		_str[_size] = '\0';
    		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    🌼memset

    void * memset ( void * ptr, int value, size_t num );
    
    • 1

    填充内存块

    将ptr指向的内存块 的前num字节设置为指定value(解释为unsigned char)。

    🌸insert()

    在pos前插入ch

    string& insert(size_t pos,char ch)
    {
    	assert(pos <= _size);
    	if(_size == _capacity)
        {
            reverse(_capacity == 0? 4:_capacity*2);
        }
        size_t end = _size+1;
        //挪动数据
        while(end>pos)
        {
        	_str[end] = _str[end-1];
        	--end;
        }
        _str[pos] = ch;
        ++_size;
        
        return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    string& insert(size_t pos, const char* s)
    {
        assert(pos <= _size);
        size_t len = strlen(s);
        if (_size + len > _capacity)
        {
            reverse(_size + len);
        }
        //挪动数据 给s留出位置
        size_t end = _size + len;
        while (end> pos+len)//小心越界,画图可知
        {
            _str[end] = _str[end - len];
            --end;
        }
        strncpy(_str + pos, s, len);
        _size += len;
        return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    🌼strncpy

    char * strncpy ( char * 目标, const char * 源, size_t num );
    
    • 1

    从字符串中复制字符

    的前num个字符 复制到目标

    🌸push_back()

    尾插

    void push_back(char ch)
    {
    	if(_size == _capacity)
        {
            //增容  为了避免capacity一开始就是0
            reverse(_capacity == 0? 4:_capacity*2);
        }
        _str[_size] = ch;
        ++_size;
        _str[_size] = '\0';
    }
    
    void push_back(char ch)
    {
     	insert(_size,ch);   
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    🌸append()

    追加字符串

    void append(const char* str)
    {
        size_t len = strlen(str);
        if (_size+len > _capacity)
        {
            reverse(_capacity * 2);
        }
        strcpy(_str + _size, str);
        _size += len;
    }
    
    void append(const char* str)
    {	
        insert(_size,str);//string& insert(size_t pos, const char* s)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    🌸find

    查找第一次出现的字符

    当指定pos时,搜索仅包括位置pos或之后的字符,返回该字符串的下标

    class luo
    {
    	size_t find(char ch)
    	{
    		for(size_t i = 0;i<_size;++i)
    		{
    			if(ch == _str[i])
    			{
    				return i;
    			}
    		}
    		return npos;
    	}
        size_t find(const char* s ,size_t pos = 0)
        {
        //ptr记录 s第一次出现的位置  _str+pos是查找的起始位置
            const char* ptr = strstr(_str + pos,s);
            //当strstr查找失败 则返回空指针
            if(ptr == nullptr)
            {
                return npos;//-1
            }
            else
            {
            	return ptr - _str;//两个指针相减的得到的是 闭区间的元素个数 0-3 = 3
            	//也就是返回了下标3
            }
        }
    
    private:
        char* _str;
        size_t _size;
        size_t _capacity; // 能存储有效字符的空间数,不包含\0
    
        static const size_t npos;
    };
    
    const size_t string::npos = -1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    测试返回的位置

    int main()
    {
    	//luo::test_string1();
    	string s1("abcdef");
    	size_t pos = s1.find('c');
    	cout << pos << endl;  //pos == 2
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    🌼strstr

    const char * strstr ( const char * str1, const char * str2 );
          char * strstr (       char * str1, const char * str2 );
    
    • 1
    • 2

    返回指向 str1 中第一次出现str2的指针,如果str2不是 str1 的一部分,则返回**空指针。 匹配过程不包括终止的空字符,但它会停在那里。

    🌸erase

    删除pos位置后的len个字符

    string& erase(size_t pos = 0, size_t len = npos)
    {
        assert(pos < _size);
        //第一种情况 要删除的数据超出了size则全部删除
        if (len == npos || len + pos > _size)
        {
            _str[pos] = '\0';//设置标识符
            _size = pos;//减少size
        }
        //第二种情况,删一部分,将后面没删完的往前挪
        else
        {
            strcpy(_str + pos, _str + pos + len);//这里会自动将之前的\0拷贝
            _size -= len; //删了len个字符,减len
        }
        return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    🌼strcmp

    int strcmp ( const char * str1, const char * str2 );
    
    • 1

    比较两个字符串

    将 C 字符串str1与 C 字符串str2进行比较。

    返回一个整数值,表示字符串之间的关系:

    返回值表示
    <0第一个不匹配的字符在ptr1中的值低于在ptr2中的值
    0两个字符串的内容相等
    >0第一个不匹配的字符在ptr1中的值大于在ptr2中的值

    💜赋值函数

    🌸operator=

    //s3 = s1
    string& operator=(const string& s)
    {
        //避免自己赋值自己 判断一下
        if (this != &s)
        {
            //先开空间,开成功再赋值,不然先delete,new失败了,原来的数据也没了
            char* tmp = new char[strlen(s._str) + 1];
            strcpy(tmp, s._str);
            delete[] _str;
            _str = tmp;
            _size = s._size;
            _capacity = s._capacity;
        }
        return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    🌸operator+=

    尾部追加字符 或 字符串

    因为 加等 的需要有返回值 例如 s2 += s3

    string& operator+=(char ch)
    {
    	push_back(ch);//复用尾插
    	return *this;
    }
    
    string& operator+=(const char* str)
    {
    	append(str);//复用追加字符串
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    🌸写了 < 和 == 其他就可以复用了

    🌸operator<

    bool operator<(const string& s1, const string& s2)
    {
        size_t i1 = 0, i2 = 0;
        while (i1 < s1.size() && i2<s2.size())
        {
            if (s1[i1] < s2[i2])
            {
                return true;
            }
            else if (s1[i1] > s2[i2])
            {
                //大于 false
                return false;
            }
            else
            {
                //相等则继续比
                ++i1;
                ++i2;
            }
        }
        // "abcd"  =  "abcd"  false
        // "abcd"  <  "abcde" true
        // "abcde" >  "abcd"  false 
        return i2 < s2.size() ? true : false;
    }
    
    bool operator<(const string& s1, const string& s2)
    {
        return strcmp(s1.c_str(), s2.c_str()) < 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    🌸operator==

    bool operator==(const string& s1, const string& s2)
    {
        return strcmp(s1.c_str(), s2.c_str()) == 0;
    }
    
    • 1
    • 2
    • 3
    • 4

    🌸operator<<

    流提取 ,范围for必须有迭代器支持

    //要有返回值 cout<<s1 -> operator(cout,s1)
    ostream& operator<<(ostream& out, const string& s)
    {
        for (auto& ch : s)
        {
            out << ch;
        }
        return out;
    }
    //写法2
    ostream& operator<<(ostream& out, const string& s)
    {
        for (size_t i = 0; i < s.size(); i++)
        {
            out << s[i];
        }
        return out;
    }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    🌸operator>>

    istream& operator>>(istream& in,string& s)//s不能是const
    {
        //先清理,不清除的话,原有的数据还保留
        s.clear();
        char ch = in.get();
        //遇到空格 换行结束
        while (ch != ' ' || ch != '\n')
        {
            s += ch;
            ch = in.get();
        }
        return in;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    如何在debian上实现一键恢复操作系统?
    Dobbo微服务项目实战(详细介绍+案例源码) - 1.项目介绍及环境配置
    window安装HBase(单机版)
    2022软件测试3大发展趋势,看看你都知道吗?
    Qt+WebAssembly学习笔记(六)win10+Qt6.4.0rc1开发环境搭建
    vue2中vant适配-把px都换算成rem
    【Vue3.0移动端项目--旅游网】-- 房东评价、热门评论、预定须知模块
    mysql操作 sql语句中的完整性约束有哪些,主键约束、外键约束、引用完整性约束,主键外键、唯一性
    解决报错之org.aspectj.lang不存在
    驱动开发课程LED点亮
  • 原文地址:https://blog.csdn.net/iluo12/article/details/125465894