c++模拟实现string类详情

目录
  • 一、string类简介
  • 二、模拟实现
    • 成员变量
    • 成员函数
    • 迭代器
    • 重载运算符[ ]
  • 三、几种常见函数
    • reserve()
    • resize()
    • push_back()
    • append()
    • 重载+=
    • insert()
    • erase()
    • find()
  • 四、操作符重载
    • 流插入<<
    • 流提取>>

一、string类简介

标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件。作为标准库的一部分,string定义在命名空间std中。

二、模拟实现

成员变量

    char* _str;
    size_t _size;
    size_t _capacity;    //不包含最后做标识的'\0'
    static const size_t npos;

_str用来存储字符串,_size表示字符串有效数据个数,_capacity表示容量大小,其中不包含最后做标识的‘\0’

例如这样一段代码:string str(“hello”);

npos是一个静态成员常量,值为-1,因为size_t是无符号整数类型,所以它表示size_t类型的最大值。
当npos用在做成员函数中len的参数时,表示“直到字符串的结尾”,
例如用来删除字符串中某一部分的函数erase(size_t pos = 0, size_t len = npos),如果没传参数len,那么就会从pos位置直接删到最后。
当作为返回值时,npos通常表示不匹配,例如find函数返回npos,就意味着没找到。

初始化string对象常用的几种方式:

    string s1("hello");         //默认初始化,s1是一个空字符串
    string s2 = s1;          //s2是s1的副本
    string s3 = "hello";
    string s4(5, 'c');       //s4的内容是ccccc

成员函数

构造函数:

  string(const char* str = "")
        :_size(strlen(str))
        ,_capacity(_size)
    {
        _str = new char[_capacity + 1];
        strcpy(_str, str);
    }

这里需要注意的是参数列表的初始化顺序与初始化列表列出的顺序无关,只与它在类中声明顺序有关,由于我们声明成员变量顺序_size_capacity前面,所以这里_size也要在_capacity前面。
容量_capacity中不包含’\0’,所以申请空间时多申请一位。
重载一个用来初始化s4的构造函数

  string(const size_t n, const char ch)
        :_size(n)
        , _capacity(_size)
    {
        _str = new char[_capacity + 1];
        for (size_t i = 0; i < n; ++i)
        {
            _str[i] = ch;
        }
        _str[_size] = '\0';
    }

拷贝构造:

    string(const string& s)
        :_str(nullptr)
    {
        string tmp(s._str);
        swap(tmp);
    }

s._str去构造临时对象tmp,这里引用传参s是s1的别名,tmp调用构造函数开空间拷贝数据,所以最后tmp和s1是一样的数据一样的大小,而tmp的空间是s2想要的,所以把他们交换。这样s2就达到深拷贝的效果了。
tmp是局部对象,出函数作用域会调用析构函数,而s2的_str指向的位置是随机值,把tmp和s2交换后tmp的_str就变成了随机值,不能对一个随机的位置进行释放,所以先在参数列表中把s2的_str指向nullptr

另外还需要提供一个swap函数交换两个对象:

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

赋值重载:

    string& operator=(string s)
    {
        swap(s);
        return *this;
    }

string s1(“hello”);
string s2(“world”);
s2 = s1;

s1传值传参给s,调用拷贝构造深拷贝,s和s1是一样的,把s和s2交换,出函数作用域后形参s调用析构函数释放资源。

析构函数:

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

迭代器

迭代器是一个像指针一样的东西,有可能是指针,也有可能不是指针。

begin()返回第一个有效数据位置的迭代器
end()返回最后一个有效数据的下一个位置的迭代器
vector/string这种底层用连续一段空间存储数据,支持[ ] + 下标访问,迭代器用原生指针即可。

普通迭代器:

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

const迭代器:

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

重载运算符[ ]

也一样提供非const版本和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];
    }

现在可以创建一个string对象并且遍历了。

三、几种常见函数

reserve()

reserve()函数用来修改字符串容量的大小。如果申请空间的newcapacity大于当前的capacity,则分配新的存储空间,并使capacity 等于或大于 newcapacity。如果newcapacity小于当前容量,则是一个非绑定收缩请求。
从C++20起如果newcapacity小于或等于当前容量,则没有效果。

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

resize()

resize()用来将字符串大小调整为n个字符的长度。
如果 n 小于当前字符串长度,则将当前size缩短为n。
如果 n 大于当前字符串长度,则通过在末尾插入所需数量的字符来扩展当前内容,以达到 n 的大小。 如果指定了字符,则将新元素初始化为该字符,否则初始化为空字符。

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

push_back()

将给定字符加到字符串的末尾。

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

append()

在字符串结尾添加字符串

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

有了push_back()append()就很方便重载+=

重载+=

右操作数为字符:

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

右操作数为字符串:

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

右操作数为对象:

    string& operator+=(const string& s)
    {
        *this += s._str;
        return *this;
    }

insert()

在字符串任意位置插入一个字符或字符串。
push_back()append()都可以复用insert()

插入字符:

    string& insert(size_t pos, char ch)
    {
        assert(pos <= _size);
        if (_size == _capacity)
        {
            size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
            reserve(newcapacity);
        }
        for (size_t i = _size + 1; i > pos; --i)
        {
            _str[i] = _str[i - 1];
        }
        _str[pos] = ch;
        ++_size;
        return *this;
    }

插入字符串:

 string& insert(size_t pos, const char* str)
    {
        assert(pos <= _size);
        size_t len = strlen(str);
        if (len + _size > _capacity)
        {
            reserve(len + _size);
        }
        for (size_t i = _size + len; i >= (pos + len); --i)
        {
            _str[i] = _str[i - len];
        }
        for (size_t i = 0; i < len; ++i)
        {
            _str[pos + i] = str[i];
        }
        _size += len;
        return *this;
    }

erase()

删除字符串的一部分,减少它的长度,如果没给参数len就会从pos位置直接删到最后。

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

find()

查找从pos位置开始第一个给定字符或字符串。找到了返回对应字符或子串第一个字符第一次出现的位置,没找到返回npos。
查找字符:

 size_t find(char ch, size_t pos = 0)
    {
        for (size_t i = pos; i < _size; ++i)
        {
            if (_str[i] == ch)
                return i;
        }
        return npos;
    }

查找字符串:

   size_t find(const char* sub, size_t pos = 0)
    {
        const char* p = strstr(_str + pos, sub);
        if (p)
            return p - _str;
        return npos;
    }

四、操作符重载

流插入<<

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

流提取>>

    istream& operator>>(istream& in, string& s)
    {
        s.clear();
        char ch = in.get();
        while (ch != ' ' && ch != '\n')
        {
            s += ch;
            ch = in.get();
        }
        return in;
    }

其他运算符重载和一些简单的函数在完整代码给出。

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

(0)

相关推荐

  • 详解C++ string字符串类

    C++字符串string类 在C语言里,字符串是用字符数组来表示的,而对于应用层而言,会经常用到字符串,而继续使用字符数组,就使得效率非常低. 所以在C++标准库里,通过类string从新自定义了字符串. 头文件: #include <string> string直接支持字符串连接 string直接支持字符串的大小比较 string直接支持子串查找和提取 string直接支持字符串的插入和替换 string同时具备字符串数组的灵活性,可以通过[ ]重载操作符来访问每个字符. 字符串数组和str

  • C++ stringstream类用法详解

    本文主要介绍 C++ 中 stringstream 类的常见用法. 1 概述 <sstream> 定义了三个类:istringstream.ostringstream 和 stringstream,分别用来进行流的输入.输出和输入输出操作.本文以 stringstream 为主,介绍流的输入和输出操作. <sstream> 主要用来进行数据类型转换,由于 <sstream> 使用 string 对象来代替字符数组(snprintf方式),就避免缓冲区溢出的危险:而且,因

  • c++编写String类代码实例

    本文实例为大家分享了c++编写String类的具体代码,供大家参考,具体内容如下 class String { public: String(const char* = nullptr); //普通构造函数 String(const String& other); //拷贝构造函数 ~String(void); //析构函数 String& operator = (const String& other); //赋值函数 private: char* m_data; }; //普通

  • C++ 系统String类详解

    目录 一.C++ 系统String类 1.定义及初始化 2.类型大小 3.常用运算 1.赋值 2.加法 3.关系 4.常见的成员函数 1.下标操作 2.求串大小 3.返回c串(c语言中的字符串也叫c串) 4.查找 5.删除 6.交换swap 5.string类型数组 总结 一.C++ 系统String类 除了使用字符数组来处理字符串以外,c++引入了字符串类型.可以定义字符串变量. 1.定义及初始化 #include <iostream> #include <string.h> u

  • C++中的string类型

    目录 1.string 类 1.1 和char *的异同 1.2 C++11初始化 1.3 拼接 1.4 长度 1.5 IO 1.6 原始字符串 1.string 类 1.1 和char *的异同 在C++当中,除了char *类型,还有专门的字符串类型,就叫做string. 通过包含头文件string就可以使用: include<string> 在很多方面,string类型的使用方法和char *一样,例如: string str1; string str2 = "hello wo

  • c++模拟实现string类详情

    目录 一.string类简介 二.模拟实现 成员变量 成员函数 迭代器 重载运算符[ ] 三.几种常见函数 reserve() resize() push_back() append() 重载+= insert() erase() find() 四.操作符重载 流插入<< 流提取>> 一.string类简介 标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件.作为标准库的一部分,string定义在命名空间std中. 二.模拟实现 成员变量

  • 详解C++中String类模拟实现以及深拷贝浅拷贝

    详解C++中String类模拟实现以及深拷贝浅拷贝 在C语言中/C++中,字符串是一个应用很广泛的类型,也是很基础的类型,C语言并没有直接处理字符串的操作而是采用字符指针和字符串数组进行操作,而在C++中标准库为我们封装了一个字符串的类供我们使用,使用需要#inlcude <string>头文件.我们也可以自己模拟实现一个简单的String类. 在模拟实现String类的过程中,不可避免的会遇到深拷贝浅拷贝的问题,下面就深拷贝浅拷贝做一个简介.所谓深拷贝浅拷贝,简单来说就是浅拷贝只是简单的将值

  • 关于C++STL string类的介绍及模拟实现

    目录 一.标准库中的string类 1.string类 2.string类中的常用接口说明+模拟实现 2.1 string类对象的常见构造+模拟实现 2.2 string类对象的容量操作+模拟实现 2.3 string类对象的访问及遍历操作+模拟实现 2.4 string类对象的修改操作+模拟实现 2.5 string类非成员函数+模拟实现 一.标准库中的string类 1.string类 字符串的表示字符序列的类 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专

  • String类的写时拷贝实例

    实例如下: #include<iostream> using namespace std; class String; ostream& operator<<(ostream &out, const String&s); //引用计数器类 class String_rep { friend class String; friend ostream& operator<<(ostream &out, const String&

  • 代码分析c++中string类

    一:回顾 (1)c++中的string类是在面试中和笔试中经常考的题目: 工程代码免费下载 string类的自行实现 (2)c++中的string类和fstream类合起来是处理外部数据的利器: (3)string类经常用到find find_first_of find_first_not_of find_last_of find_last_not_of substr replace等,以及联合使用来达到java中的split和trim (4) 使用friend 仅仅是在类中进行声明的非内部 却

  • java String类功能、原理与应用案例【统计、判断、转换等】

    本文实例讲述了java String类功能.原理与应用.分享给大家供大家参考,具体如下: String构造方法 package cn.itcast_01; /* * 字符串:就是由多个字符组成的一串数据.也可以看成是一个字符数组. * 通过查看API,我们可以知道 * A:字符串字面值"abc"也可以看成是一个字符串对象. * B:字符串是常量,一旦被赋值,就不能被改变. * * 构造方法: * public String():空构造 * public String(byte[] by

  • Java 中的 File类详情

    目录 一.File类简单介绍 二. 路径的分类 三. 构造方法 四. 成员方法 一.File类简单介绍 为什么要学习File类呢,他的作用又是什么呢? IO流操作中大部分都是对文件进行操作,所以Java就提供了一个File类供我们来操作文件,它是以抽象的方式代表文件名和目录路径名,该类主要是对文件或目录的创建,文件的查找,删除等.对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已.它可以是存在的,也可以是不存在的.将来是要通过具体的操作把这个路径的内容转换为具体存在的. 二

  • C++深浅拷贝和string类的两种写法详解

    目录 一.深浅拷贝 二.string类的两种写法 1.传统写法 2.现代写法 总结 一.深浅拷贝 拷贝这个词对于我们来说应该不陌生,比如我们平常的复制和粘贴就是拷贝:但是如果把拷贝这个词放到C++中来说就有一些复杂了,我们先来看一下什么是浅拷贝: 下面用字符串类来模拟实现. class Astring { public: //构造函数 Astring(const char* str = "") { _str = new char[strlen(str) + 1]; strcpy(_st

  • 深入Java万物之母Object类详情

    目录 前言 一.接口补充知识 二.Object类 1.重点一 2.重点二 3.重点三 4.重点四 总结 前言 在Java中,有一个很神奇的类,所有的类见了它都得恭敬的叫一声“父亲”,因为它就是Java中的万物之父,Object类,它是所有类的默认父类 ,子类不用使用extends关键字继承它,不管是JDK中的类,还是自定义的类 一.接口补充知识 1.接口和接口之间继承的,接口坚决不能继承一个类 2.如果一个类既需要继承一个类,又需要实现多个接口时先使用extends继承一个类,再使用implem

  • C++实现String类的方法详解

    目录 前言 string模拟实现 string简单实现 string完整实现 完整代码 前言 在C语言中,没有专门用来表示字符串的类型.C语言的字符串是一系列以’\0’为结尾的字符的集合.虽然C语言为这样的字符串提供了一系列的库函数如strcpy, strcmp等等,但这些函数与字符串这个类型是分开的,这不太符合C++中面试对象的思想,所以在C++中封装了一个string类,来帮助我们操作字符串.string该如何使用,我这里就不做赘述了,大家可以去看看官方文档呀 string - C++ Re

随机推荐