一. 环境

Linux x86_64,g++ 8.5.0

二. 实现

自实现 string 之前一直想写来着,一直拖着,现在把它完稿。这个版本是比较简单的版本,有一些可能有不同的或者更好的实现方式,后面有机会会加到里面。

打算实现的接口如下

class MyString
{
friend std::ostream & operator<<(std::ostream & co, const MyString &ms);
friend std::istream & operator>>(std::istream & ci, MyString &ms);
public:
MyString(const char * s = nullptr);
~MyString();
MyString(const MyString & another);
MyString & operator=(const MyString & another); MyString operator+(const MyString & another);
MyString & operator+=(const MyString & another);
bool operator>(const MyString & another);
bool operator<(const MyString & another);
bool operator==(const MyString & another);
char & operator[](int n);
char & at(int n);
private:
char * m_str;
};
  1. 构造函数,参数使用默认参数,默认参数作标记位。不论是否传递实参,申请资源时均以数组形式申请。不传递实参时,申请一个 char 的数组,有传递实参时,以实际的为准。这样,在释放资源时,均可以 delete []m_str 形式释放。
MyString::MyString(const char *str)
{
if (nullptr == str)
{
m_str = new char[1];
*m_str = '\0';
}
else
{
m_str = new char[strlen(str)+1];
strcpy(m_str, str);
}
}
  1. 拷贝赋值,使用了两种方式。

第一种是基础的写法,先 delete 堆上的空间,再申请新的空间,然后复制内容。需要注意的是,需判断是否是自赋值的情况。

第二种采用了 copy && swap 技术,相对完善一点,前一种方式,在 delete []m_str 后如果程序出现异常,此时其它地方有使用到 m_str 的话就尴尬了,而后一种方式就没有这个问题。

// version1
/*
MyString & MyString::operator=(const MyString &another)
{
if (this == &another)
{
return *this;
} delete []m_str;
int len = strlen(another.m_str);
m_str = new char[len+1];
strcpy(m_str, another.m_str); return *this;
}
*/ // version2,采用 copy and swap 技术
MyString & MyString::operator=(const MyString &another)
{
if (this == &another)
{
return *this;
} MyString ms(another);
std::swap(this->m_str, ms.m_str); return *this;
}
  1. 重载 + 运算符,成员函数返回一个临时对象。在申请新的空间后,在使用 strcat() 之前需要初始化,否则可能会出现问题。strcat() 是从末尾为 '\0' 的地方开始拼接的。
MyString MyString::operator+(const MyString &another)
{
MyString ms; int len = strlen(this->m_str) + strlen(another.m_str);
delete []ms.m_str;
ms.m_str = new char[len +1]{0}; // 注意初始化
strcat(strcat(ms.m_str, this->m_str), another.m_str); return ms;
}
  1. 重载 += 运算符,返回值类型是引用类型,这样可以连续使用 +=

使用 realloc() 后,在使用 strcat() 连接两个字符串之前,需要将 m_str 后面一部分新扩充的空间进行初始化。

MyString & MyString::operator+=(const MyString &another)
{
int lenOfSource = strlen(this->m_str);
int lenOfAnother = strlen(another.m_str);
this->m_str = (char *)realloc(this->m_str, lenOfSource+lenOfAnother+1);
memset(this->m_str+lenOfSource, 0, lenOfAnother+1);
strcat(this->m_str, another.m_str); return *this;
}
  1. 重载 > 运算符
bool MyString::operator>(const MyString &another)
{
return strcmp(this->m_str, another.m_str) > 0;
}

重载 <== 与上面类似,就不重复列举了。

  1. 重载 [] 运算符,这个没啥好说的了。
char & MyString::operator[](int n)
{
return m_str[n];
}
  1. 成员函数 at()
char & MyString::at(int n)
{
return m_str[n];
}
  1. 重载输出 << 和 输入 >> 运算符。

在测试成员函数前,可以早点写这两个函数,测试时就方便打印了,不然还需要单独添加一个成员函数返回 m_str 了。

重载运算符,目标形式是:

Mystring ms;
cout << ms;
cin >> ms;

对于重载,一般会考虑到成员函数重载和全局重载,但是 ostream 类和 istream 类都是系统提供的类,我们不可能在 ostream 类和 istream 类中进行修改,因此只能放弃成员函数重载。此时,只能是全局重载,即全局函数重载了。

考虑到会连续输出(cout << a << b;),因此返回类型是 ostream & 类型,它是经入参而来,入参类型也是 ostream &

std::ostream & operator<<(std::ostream & co, const MyString &ms)
{
co << ms.m_str;
return co;
}

输入运算符与输出运算符类似,第二个入参不能是 const 类型,因为需要修改入参 ms。这里处理的相对简单了,栈上申请了1024字节的字符数组用以存储输入的数据,实际上会有不够用的情况。

std::istream & operator>>(std::istream & ci, MyString &ms)
{
// 简单处理,申请一块固定大小的内存
char ch[1024];
ci >> ch;
delete []ms.m_str;
ms.m_str = new char[strlen(ch)+1];
strcpy(ms.m_str, ch);
return ci;
}

三. 完整代码,可点击链接 mystring ,如有有问题或不到之处,请指出并交流,看到后我会修改。

四. 参考

C++基础与提高 王桂林

自实现string类的更多相关文章

  1. 标准库String类

    下面的程序并没有把String类的所有成员方法实现,只参考教程写了大部分重要的成员函数. [cpp] view plain copy #include<iostream> #include ...

  2. 自己实现简单的string类

    1.前言 最近看了下<C++Primer>,觉得受益匪浅.不过纸上得来终觉浅,觉知此事须躬行.今天看了类类型,书中简单实现了String类,自己以前也学过C++,不过说来惭愧,以前都是用C ...

  3. C++ string类的实现

    c++中string类的实现 今天面试被考到了, 全给忘记了!!!   //string类的实现 #include <iostream> #include <string.h> ...

  4. String类的功能

    String类              标红的为较少出现的 1.判断功能 boolean equals(Object obj) :比较字符串内容是否相同,区分大小写 boolean equalsIg ...

  5. java基础复习:final,static,以及String类

    2.final 1)为啥String是final修饰的呢? 自己答: 答案: 主要是为了“效率” 和 “安全性” 的缘故.若 String允许被继承, 由于它的高度被使用率, 可能会降低程序的性能,所 ...

  6. String类和StringBuffer类的区别

    首先,String和StringBuffer主要有2个区别: (1)String类对象为不可变对象,一旦你修改了String对象的值,隐性重新创建了一个新的对象,释放原String对象,StringB ...

  7. 05_整理String类的Length()、charAt()、 getChars()、replace()、 toUpperCase()、 toLowerCase()、trim()、toCharArray()使用说明

    Question: 整理String类的Length().charAt(). getChars().replace(). toUpperCase(). toLowerCase().trim().toC ...

  8. 标准C++中的string类的用法总结

    标准C++中的string类的用法总结 相信使用过MFC编程的朋友对CString这个类的印象应该非常深刻吧?的确,MFC中的CString类使用起来真的非常的方便好用.但是如果离开了MFC框架,还有 ...

  9. String类常用方法

    1.String类的特点,字符串一旦被初始化就不会被改变. 2.String对象定义的两种方式 ①String s = "affdf";这种定义方式是在字符串常量池中创建一个Str ...

  10. 运用String类实现一个模拟用户登录程序

    package Test; import java.util.Scanner; // 模拟用户登录程序 // 思路: // 1.用两个String类分别接收用户名和密码 // 2.判断输入的用户名和密 ...

随机推荐

  1. 线上问题排查--进程重启失败,最后发现是忘了cd

    背景 我前面写了几篇文章,讲c3p0数据库连接池发生了连接泄露,但是随机出现,难以确定根因,最终呢,为了快速解决问题,我是先写了个shell脚本,脚本主要是检测服务的接口访问日志,看看过去的30s内是 ...

  2. vxe-table中文文档

    https://xuliangzhan_admin.gitee.io/vxe-table/#/table/grid/fullEdit

  3. Linux系统启动jmeter可视化界面

    目的:方便在Linux系统调试jmeter脚本.操作:Linux启动jmeter可视化界面,不能使用root用户,需要创建新用户,切换到新用户启动jmeter. 1. 创建用户 创建一个新的系统用户. ...

  4. 关于 LLM 和图数据库、知识图谱的那些事

    本文整理自 NebulaGraph 布道师 wey 在「夜谈 LLM」主题分享上的演讲,主要包括以下内容: 背景 LLM RAG Graph 知识抽取 Text2Cypher Graph RAG 未来 ...

  5. 震惊!强大的接口自动化测试框架2.0,unittest与pytest无缝穿插对接,可以像postman一样编写代码

    theme: fancy highlight: arta 项目介绍 接口自动化测试项目2.0 软件架构 本框架主要是基于 Python + unittest + ddt + HTMLTestRunne ...

  6. 使用 Laf 一周内上线美术狮 AI 绘画小程序

    "美术狮 AI 绘画"(以下简称"美术狮"),是我们小团队的一次尝试,定位是人人都可以上手的,充满创意的,理解中文和中国文化的图片生成工具. 在完善图像模型和论 ...

  7. Codeforces 1257D - Yet Another Monster Killing Problem

    题意: 有\(n\)个怪物,每个怪物有攻击力\(a_i\)点:有\(m\)个英雄,每个英雄有攻击力\(p_i\)点,耐力\(s_{i}\)点. 怪物需要被依次杀死(按输入顺序). 每一天可以挑选一个英 ...

  8. 对比 MyBatis 和 MyBatis-Plus 批量插入、批量更新的性能和区别

    1 环境准备 demo 地址:learn-mybatis · Sean/spring-cloud-alibaba - 码云(gitee.com) 1.1 搭建 MyBatis-Plus 环境 创建 m ...

  9. Python/Java/Php/C#/Go/C/C++这几个主力语言,谁到底真的不行

    1.前言 阿里最近又进行了史诗级的大裁员,IT行业肉眼可见的持续性衰退与没落.当潮水退却,才能看出谁在裸泳.作为当今计算机编程界的几大主力语言,谁才真正的裸泳者呢? 2.描述 1.Python: Py ...

  10. MySQL实战实战系列 02 日志系统:一条SQL更新语句是如何执行的?

    前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块.相信你还记得,一条查询语句的执行过程一般是经过连接器.分析器.优化器.执行器等功能模块,最后到达存储引擎. 那么,一条更新语 ...