广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++模拟实现string的方法详解
  • 442
分享到

C++模拟实现string的方法详解

C++实现stringC++ string 2022-11-13 19:11:41 442人浏览 薄情痞子
摘要

目录1.string 成员变量2.构造函数3.拷贝构造、赋值重载和析构函数1.拷贝构造2.赋值重载3.析构函数4.访问成员变量5.遍历1.下标+【】2.迭代器(iterator)3.

1.string 成员变量

首先需要一个动态开辟的指针指向这个字符串,然后还需要容量和存储的个数,并且我们不能和标准库的string进行冲突所以我们需要写在我们自己的类域中,并且我们库中还有一个静态的变量是npos,就是无符号的-1,代表整形的最大值:

namespace cyf
{
    class string
    {
    public:
        //成员函数
    private:
        char *_str;
        size_t size;
        size_t capaticy;
        const static size_t npos = -1;
    };
}

这里有一个特例:static成员变量一般在类中声明在类外定义,但是const static int型的变量可以直接在类中定义。

2.构造函数

strlen求出的是\0之前的字符个数,所以_size和_capacity标识的是实际存储的字符个数,在开辟空间时多开辟一个字符用来存储'\0'。

string(const char* s = "")  
        {
            _size = strlen(s);  
            _capacity = _size;
            _str = new char[_capacity + 1];   
 
            strcpy(_str, s);  //开辟好空间后将s的内容拷贝至_str
        }

3.拷贝构造、赋值重载和析构函数

1.拷贝构造

_str 维护的是一块空间,所以不能简单的将s._str的值赋值给_str (浅拷贝),而是单独开辟一块空间,让_str指向这一块空间,再将s._str空间中的值拷贝至新开辟的空间,新开辟的空间比_capacity多开一个字节用来存储'\0',作为字符串的结束标志。

//string s1(s)
string(const string& s)
        {
            _size = s._size;
            _capacity = s._capacity;
            _str = new char[s._capacity + 1];
 
            strcpy(_str, s._str);
        }

2.赋值重载

首先开辟一块空间,将字符串的内容拷贝至这个空间,将_str原来指向的空间释放,_str再指向这个新开辟的空间,size和capacity还是原来的大小。

//s2=s1
        string& operator=(const string& s)
        {
            if (this != &s)   //避免自己给自己赋值
            {
                char* tmp = new char[s._capacity + 1];
                strcpy(tmp, s._str);
                delete[] _str;
                _str = tmp;
                _size = s._size;
                _capacity = s._capacity;
            }
            return *this;
        }

3.析构函数

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

4.访问成员变量

提供接口可以在类的外边查看字符串的内容,存储字符串的元素个数和容量

        const char* c_str()
        {
            return _str;
        }
 
        size_t size()
        {
            return _size;
        }
        size_t capacity()
        {
            return _capacity;
        }

配合之前的构造函数和这里的接口,我们进行验证:

运行结果:

5.遍历

遍历有三种模式:

1.下标+【】

_str是一个指针,那么我们可以通过数组的方式来访问,只需要重载operator []即可。我们还是要重载两个版本的,因为普通变量和const变量的访问权限不一样。

//普通变量,可读可写
char& operator[](size_t pos)
{
    assert(pos < _size);  //检查不能越界访问
 
    return _str[pos];
}
 
//const变量,只读属性
char& operator[](size_t pos) const
{
    assert(pos < _size);
 
 
    return _str[pos];
}

2.迭代器(iterator)

在string中,迭代器就是一个指针,只不过我们进行了封装,typedef一下就可以啦,同样我们也要实现两个版本的,const和非const的。

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

3.范围for

我们范围for的底层就是迭代器,所以我们不用实现,只要实现了迭代器,那么我们就可以直接使用范围for,范围for在执行的时候实际还是通过迭代器实现的,上例子:

运行结果:

6.空间的申请

1.reserve

一般是我们原空间容量满了,需要申请空间扩容,我们的扩容函数还是要先申请空间,然后在进行拷贝,接着我们delete原来的空间,把申请的空间的指针和 容量 赋值过去即可。 

void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char* tmp = new char[n + 1];    //多开一个字节留给'\0';
                strcpy(tmp, _str);
                delete[] _str;
                _str = tmp;
 
 
                _capacity = n;
            }
 
 
        }

2.resize

1. 如果我们是传入一个正整数大于_size的值,那么我们可以使用传入的字符(或者缺省值)把我们申请的空间进行初始化,也就是从_size到n-1置为我们传入的字符,n置为' \0 ',最后把_size置为n。

2.如果传入一个小于_size正整数,那么我们把0~_size-1进行初始化为传入的字符(或者缺省值),把n位置置为' \0 ',接着我们会把_size置为n,而_capaticy不变。

void resize(size_t n, char ch = '\0')
        {
            if (n > _size)
            {
                reserve(n);
                for (size_t i = _size; i < n; ++i)
                {
                    _str[i] = ch;
                }
                _size = n;
                _str[_size] = '\0';
 
            }
 
            else   //小于
            {
                _str[n] = '\0';
                _size = n;
 
            }
 
    }

7.增删查改

1.push_back   尾插一个字符

上来就检查容量,_size==_capacity时就说明没有容量了,得分类讨论:1.原来的字符串有元素2.原来的是空字符串,如果字符串为空,就给4个字节大小的容量 。如果原来的字符串不为空但是需要扩容就调用reserve函数进行扩容,容量扩为2倍,2倍比较合适,避免给的小了频繁的扩容,但是也不能给的太大了,太大了会造成空间的浪费。在_size的位置插入字符ch ,_size++,插入的字符ch 将原来的'\0'给覆盖了,最后再补上'\0',作为字符串的结束标志。

void push_back(char ch)
        {
            if (_size == _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
                reserve(newcapacity);
            }
            _str[_size] = ch;   
            ++(_size);
            _str[_size] = '\0'; //记得处理 \0
        }

2.append 尾部插入一个字符串

插入字符串的大小不确定,就需要确定是否需要扩容。当插入的字符串的长度加上当前字符串的有效元素个数大于容量_capacity时,就需要扩容,扩后的容量大小为_size+len ,这里给reverse函数传的是有效元素的个数,在reverse函数内部为我们多开了一个字符的大小用来存储'\0'。再进行拷贝工作,这里记得插入元素后_size要进行变换。

void append(const char* str)
        {
            size_t len = strlen(str);
            if (len + _size > _capacity)
            {
                reserve(_size + len);   
 
            }
            strcpy(_str + _size, str);
            _size += len;
        }

3.insert  在指定位置插入一个字符

上来首先检查要插入的位置是否合理,_size 代表的位置是有效元素的下一个位置即'\0'的位置,pos的范围【0,_size】string字符串的头到尾部之间 ,插入元素要检查容量,记得考虑原string是否为空串的情况。插入数据需要挪动数据,从前往后挪动数据,将end位置确定到'\0'的下一个位置,这样方便头插。最终将pos位置腾出来,插入字符ch 插入数据后_size++。

string& insert(size_t pos, char ch)
        {
            assert(pos <= _size);  //等于size 时候相当于尾部插入
            if (_size == _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
                reserve(newcapacity);
            }
            size_t end = _size + 1;  //这里是把\0往  \0的下一个位置挪动 方便头插   
            while (end > pos)
            {
                _str[end] = _str[end - 1];
                --end;
            }
            _str[pos] = ch;
            _size++;
            return *this;
        }

4.insert 在指定位置插入一个字符串

string& insert(size_t pos, const char* str),我们先进行断言pos不能超过_size,接着我们开辟空间,这次就不考虑空串的问题了,因为我们要指定开辟的字节数,和上面一样的我们也要进行挪动数据,我们只不过是由每次挪动一个步,变为了挪动 len 步了,最后使用strncpy插入字符串,把_szie +=len 即可。

画图理解:

string& insert(size_t pos, const char* str)
        {
            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }
            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;
        }

实现了上述的接口当然最好用的还是下面的接口,对push_back和append进行封装实现string+=

一个字符 和 string+=一个字符串

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

5.删除接口:erase

指定字符串从开始位置到指定位置删除元素。得保证删除的位置在string的内部,当只给定了删除的起始位置没有给结束的位置那么就触法我们的缺省值,即从pos位置开始直到将字符串删完,或者说给定的结束位置大于了字符串的本身长度,那就从pos位置开始直到删除完字符串,实现的方法很简单,直接在pos位置添字符串的结束标志'\0'。  如果给定的两个值都在字符串的内部直接进行从len位置往前拷贝覆盖掉要删除的元素。

string& erase(size_t pos, size_t len = npos)  
        {
            assert(pos <= len);
            if (len == npos || len >= _size - pos)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
                strcpy(_str + pos, _str + pos + len);
                _size -= len;
 
            }
            return *this;
        }

6.find字符 从某个位置开始查找字符,如果没有给定开始位置,就用缺省值,默认从开头寻找,找到了就返回元素的下标,没有找到就返回npos。

size_t find(char ch, size_t pos = 0)  ///默认从pos位置开始寻找,有缺省值0,从pos 位置开始往后寻找
        {                                     //对比找到了就返回下标,找不到返回npos
            assert(pos < _size);
            while (pos < _size)
            {
                if (_str[pos] == ch)  //遍历寻找
                {
                    return pos;
                }
                pos++;
            }
            return npos;
        }

7.find字符串  ,从某个位置开始往后寻找字符串,找到了就返回下标,找不到就返回npos

这里套用C语言的库函数strstr进行实现

size_t find(const char* str, size_t pos = 0)
        {
            assert(pos < _size);
            const char* ptr = strstr(_str + pos, str);   
            if (ptr == nullptr)  //strstr找不到返回空指针
            {
                return npos;    //转换至cpp找不到就返回npos
            }
            else
            {
                return ptr - _str;  //返回的是下标  指针-指针  ==下标
            }
 
        }

8.clear  清空字符串的内容

直接在第一个位置加入结束标志就将字符串清空了,将清空后_size就为0,

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

8.重载cin 和 cout

1.cout 

依次的输出string对象的内容即可

    ostream& operator<<(ostream& out,const string& s)
    {
        for (size_t i = 0; i < s.size(); i++)
        {
            out << s[i];
        }
 
        return out;
    }

2.cin

这里注意因为要改变字符串的内容,首先调用clear函数清空原来的内容,因为要改变字符串的内容所以不用const 直接引用改变的就是字符串的本身。因为我们的 in 会默认 '空格' 和 ' \n '是分割符,不进行读取,这样我们就没办法停止。需要使用下 in 的get函数,让我们的来读取 ‘  ’  和         ' \n ',我们看下代码:

void clear()
{
    _str[0] = '\0';
    _size = 0;
}
 
istream& operator>>(istream& in, string& s)
{
    s.clear();//要先进行清理,否则就会出现剩余的数据也被我们写入了。
    char ch;
    ch = in.get();
 
    char buff[32];
    size_t i = 0;
 
    while (ch != ' ' && ch != '\n')
    {
        buff[i++] = ch;
        if (i == 31)
        {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        ch = in.get();
    }
 
    buff[i] = '\0';
    s += buff;
 
    return in;
}

cin和cout的重载不一定是类的友元函数,在类中提供接口,我们也可以直接访问类的成员变量!

到此这篇关于c++模拟实现string的方法详解的文章就介绍到这了,更多相关C++实现string内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++模拟实现string的方法详解

本文链接: https://www.lsjlt.com/news/171229.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++模拟实现string的方法详解
    目录1.string 成员变量2.构造函数3.拷贝构造、赋值重载和析构函数1.拷贝构造2.赋值重载3.析构函数4.访问成员变量5.遍历1.下标+【】2.迭代器(iterator)3....
    99+
    2022-11-13
    C++实现string C++ string
  • c++模拟实现string类详情
    目录一、string类简介二、模拟实现成员变量成员函数迭代器重载运算符[ ]三、几种常见函数reserve()resize()push_back()append()重载+=inser...
    99+
    2022-11-12
  • C++String部分成员模拟实现流程详解
    目录string类的成员设计普通构造函数的模拟拷贝构造函数的模拟赋值重载函数的模拟String的析构函数模拟补全上述的成员函数迭代器的简单模拟其他成员函数的模拟string类的成员设...
    99+
    2022-11-13
  • C++模拟实现vector的方法
    今天小编给大家分享一下C++模拟实现vector的方法的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 模拟实现vecto...
    99+
    2023-07-02
  • C++哈希表之闭散列方法的模拟实现详解
    目录哈希概念冲突闭散列线性探测哈希表闭散列的模拟实现模拟实现的闭散列中的问题与改进哈希 概念 可以不经过任何比较,直接从表中得到要搜索的元素。 关键在于通过某种函数,使元素的存储位置...
    99+
    2022-11-13
    C++哈希表实现闭散列 C++ 闭散列 C++哈希表
  • 详解C++STL模拟实现forward_list
    目录forward_list 概述接口总览forward_list 的节点默认成员函数默认构造函数析构函数forward_list 的迭代器构造函数operator==operato...
    99+
    2023-01-13
    C++ STL实现forward_list C++ STL forward_list
  • 详解C++ STL模拟实现list
    目录list 概述接口总览list 的节点默认成员函数默认构造函数析构函数拷贝构造函数复制赋值函数list 的迭代器构造函数operator==operator!=operator*...
    99+
    2023-01-11
    C++ STL实现list C++ STL list
  • C++ vector类的模拟实现方法
    vector和string虽然底层都是通过顺序表来实现的,但是他们利用顺序表的方式不同,string是指定好了类型,通过使用顺序表来存储并对数据进行操作,而vector是利用了C++...
    99+
    2022-11-12
  • C++中vector的模拟实现实例详解
    目录vector接口总览 默认成员函数 构造函数 拷贝构造 赋值重载 析构函数 迭代器相关函数 begin和end 容量相关函数 size和capacity reserve resi...
    99+
    2022-11-12
  • C++ stack与queue模拟实现详解
    目录stack与queue模拟实现 stackqueue为什么选择deque作为stack和queue的底层默认容器总结stack与queue模拟实现 在stl中,stack(...
    99+
    2022-11-12
  • C++模拟实现vector流程详解
    目录模拟vector总结模拟vector 我们可以通过模板实现类似vector的类。我们实现一个StrVecTemp类,其内部通过allocator开辟空间,存储的类型用T来表示,T...
    99+
    2022-11-13
    C++ vector容器 C++ vector
  • C++模拟实现List迭代器详解
    目录概念迭代器使用迭代器模拟实现迭代器的大体结构构造函数解引用重载重载自增实现自减实现运算符重载迭代器失效模拟List概念 迭代器是一种抽象的设计概念,其定义为:提供一种方法,使他能...
    99+
    2022-11-13
  • C++超详细讲解模拟实现vector
    目录1. 模拟实现vector2. vector常用接口2.1 reserve2.2 resize2.3 push_back2.4 pop_back()2.5 insert2.6 e...
    99+
    2022-11-13
  • C++详细讲解模拟实现位图和布隆过滤器的方法
    目录位图引论概念解决引论位图的模拟实现铺垫结构构造函数存储set,reset,testflip,size,countany,none,all重载流运算符测试位图简单应用位图代码汇总布...
    99+
    2022-11-13
  • 详解C++中vector的理解以及模拟实现
    目录vector介绍vector常见函数介绍vector模拟实现及迭代器失效讲解vector介绍 vector文档 1.vector是表示可变大小数组的序列容器。 2.就像数组一样,...
    99+
    2023-03-08
    C++ vector实现 C++ vector
  • C++详细讲解stack与queue的模拟实现
    目录容器适配器双端队列概念结构deque迭代器优缺点stack模拟queue模拟实现容器适配器 适配器是一种设计模式(设计模式是一套反复使用的、大部分人知道的代码设计经验的总结),该...
    99+
    2022-11-13
  • 【C++精华铺】10.STL string模拟实现
    1. 序言         STL(标准模板库)是一个C++标准库,其中包括一些通用的算法、容器和函数对象。STL的容器是C++ STL库的重要组成部分,它们提供了一种方便的方式来管理同类型的对象。其中,STLstring是一种常用的字符串...
    99+
    2023-09-11
    c++ 开发语言 stl 数据结构
  • C++单例模式的几种实现方法详解
    目录局部静态变量方式静态成员变量指针方式智能指针方式辅助类智能指针单例模式通用的单例模板类总结局部静态变量方式 //通过静态成员变量实现单例 //懒汉式 class Single2 ...
    99+
    2022-11-13
  • C语言模拟实现库函数详解
    目录前言1.字符串函数1.1字符串控制函数1.1.1 strlen的模拟1.1.2 str(n)cpy的模拟1.1.3 str(n)cmp的模拟1.1.4 str(n)cat的模拟1...
    99+
    2022-11-13
  • C语言 模拟实现strlen函数详解
    目录前言一.strlen函数的介绍1.strlen函数的声明2.strlen函数的简单运用3.注意事项二.三种实现strlen函数的方法1.计数器的方法2.递归方法3.指针-指针的方...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作