MyString 类是学习 C++ 的过程中一个很重要的例子,涉及到面向对象的封装、堆内存申请和释放、函数的重载以及 C++ 的 “Big Three”。本例子重点在于复习和理解上述的 C++ 特性,实现的功能并不多。

MyString 类的 Header

MyString 的声明中包含了一个带指针的 C++ 类应有的函数,并且包含了一些常用的功能。其中终点讨论一下用 friend 关键字修饰的两个函数,它们是用来提供 ostream 的输出的。声明为 friend 是为了使 ostream 的对象(例如 cout)可以访问到 MyString 的成员。而这两个函数不声明为内部方法的原因是我们一般写输入输出语句都是将流对象写在前,如果声明成了成员函数则调用的时候要写成这样:

MyString str;
str.operator<<(cout);

这看起来相当怪异,所以我们希望写成 cout << str 这样。这句实际上就是 cout 对象调用重载的运算符,也可以写在实例化 cout 对象的类 ostream 里,但是 ostream 一般来说我们并不会去改动这个文件,所以写了一个全局函数来将 ostream 和 MyString 联系起来。

class MyString {
friend std::ostream &operator<<(std::ostream &os, const MyString &str);
friend std::ostream &operator>>(std::ostream &os, MyString &str);
public:
MyString(const char *cstr = nullptr);
// Big Three
MyString(const MyString &str);
MyString &operator=(const MyString &str);
~MyString(); char *get_c_str() const { return m_data; } // operator reload
MyString &operator+(const MyString &str);
bool operator==(const MyString &str); size_t length() const;
private:
char *m_data;
};

C++ 的构造函数和 Big Three

C++ 的 Big Three 包括拷贝赋值、拷贝构造和析构函数。Big Three 主要针对带有指针的类,类中的指针一般指向类所管理的一些资源,可能是该类自己申请的内存或者创建的某种对象。类维护着这些资源,负责它们的生老病死——资源的创建、使用和销毁。

一个类中如果带有着指针,那么必须要自己重写有拷贝构造和拷贝赋值函数。如果不自己重写,那么会采取默认的行为,即逐位拷贝。采取逐位拷贝的对象内部的指针也被拷贝过去,称为浅拷贝。我们希望拷贝一个对象,其管理的资源也一并拷贝一份,称为深拷贝。为了实现深拷贝,重写的拷贝赋值、拷贝构造函数中应实现资源复制的行为。而在对象生命周期结束后,其管理的资源也应该关闭或者销毁,故也需要重写析构函数。

C++ 的这三个函数紧密地联系在了一起,具体体现为逻辑上如果需要重写其中任何一个函数,那么另外两个函数也应该被重写

inline
MyString::MyString(const char *cstr = nullptr) {
if (cstr) {
m_data = new char[strlen(cstr) + 1];
strcpy(m_data, cstr);
}
else {
m_data = new char[1];
*m_data = '\0';
}
} inline
MyString::~MyString() {
delete[] m_data;
} inline
MyString::MyString(const MyString &str) {
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
} inline
MyString &MyString::operator=(const MyString &str) {
if (this == &str) return *this;
delete[] m_data;
m_data = nullptr;
m_data = new char[strlen(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this;
}

在上面的实现中,有两个值得注意的地方,一个是在拷贝赋值运算符重载中的自赋值检查,在拷贝赋值运算符函数中,如果不进行检查,按照逻辑来说,会释放被赋值对象的资源,如果是同一个对象的自我赋值,则会产生严重的后果:字符串资源被删除,两个对象指向被回收了的内存,当访问这个字符串时会出现不可预料的后果。可见,防御式编程不仅要求程序对黑客行为的输入的那些很离谱的数据做防御,还要对用户可能出现的错误进行防御。

另外一个值得注意的地方就是重载的等于操作符,用户在使用的时候有时可能会对字符串连续赋值,就像使用 cout 连续输出一样,这种类似于函数的链式调用。实现的时候只要返回对象的引用就可以了。

输入输出运算符重载

除了链式调用需要返回对象引用的这个主意点外,输入输出函数的第二个参数的 const 属性也应该主意的。具体来说输出函数的第二个参数不会被改变,我们应该将它声明为 const 的;而输入函数的第二个对象正是我们希望改变的对象,希望将数据输入到这个对象中,它应该是非 const 引用。

std::ostream &operator<<(std::ostream &os, const MyString &str) {
os << str.m_data;
return os;
} std::ostream &operator>>(std::ostream &os, MyString &str) {
os >> str.m_data;
return os;
}

至此一个非常简单的 C++ MyString 类已经实现完成了,在 STL 中,string 类的实现更加复杂,包括了正则在内的高级功能。这个例子仅仅为了学习 C++ 的一些特性,还不够深入,希望未来能专门研究一下 string 的实现。

--------------------- 本文来自 ProJ7-Jeffy 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u013040821/article/details/80455108?utm_source=copy

MyString类的实现--基础中的基础C语言的更多相关文章

  1. [.net 面向对象编程基础] (3) 基础中的基础——数据类型

    [.net 面向对象编程基础] (3) 基础中的基础——数据类型 关于数据类型,这是基础中的基础. 基础..基础..基础.基本功必须要扎实. 首先,从使用电脑开始,再到编程,电脑要存储数据,就要按类型 ...

  2. [.net 面向对象编程基础] (4) 基础中的基础——数据类型转换

    [.net面向对象编程基础] (4)基础中的基础——数据类型转换 1.为什么要进行数据转换? 首先,为什么要进行数据转换,拿值类型例子说明一下, 比如:我们要把23角零钱,换成2.30元,就需要把整形 ...

  3. [.net 面向对象编程基础] (5) 基础中的基础——变量和常量

    [.net面向对象编程基础]  (5) 基础中的基础——变量和常量 1.常量:在编译时其值能够确定,并且程序运行过程中值不发生变化的量. 通俗来说,就是定义一个不能改变值的量.既然不能变动值,那就必须 ...

  4. [.net 面向对象编程基础] (7) 基础中的基础——流程控制语句

    [.net 面向对象编程基础] (7) 基础中的基础——流程控制语句 本来没有这一节的内容,后来考虑到既然是一个系列文章,那么就尽可能写的详细一些,本节参考了网上朋友所写的例子,为的是让更多小伙伴学习 ...

  5. [.net 面向对象编程基础] (8) 基础中的基础——修饰符

    [.net 面向对象编程基础] (8) 基础中的基础——修饰符 在进入C#面向对象核心之前,我们需要先对修饰符有所了解,其实我们在前面说到变量和常量的时候,已经使用了修饰符,并且说明了变量和常量的修改 ...

  6. [.net 面向对象编程基础] (6) 基础中的基础——运算符和表达式

    [.net 面向对象编程基础] (6) 基础中的基础——运算符和表达式 说起C#运算符和表达式,小伙伴们肯定以为很简单,其实要用好表达式,不是一件容易的事.一个好的表达式可以让你做事半功倍的效果,比如 ...

  7. 从头开始学JavaScript 笔记(一)——基础中的基础

    原文:从头开始学JavaScript 笔记(一)--基础中的基础 概要:javascript的组成. 各个组成部分的作用 . 一.javascript的组成   javascript   ECMASc ...

  8. C++ 基础中的基础 ---- 引用

    C++ 基础中的基础 ---- 引用 引用的概念:引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字.一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量.比如: int n ...

  9. python之基础中的基础(一)

    python是一个效率极高的语言,现在市面上的机器学习大部分是由python和R语言完成,所以在不久之前小仙心中便种下了学习python的想法.下面是这一个月多月以来学习的总结,都是基础中基础了. 1 ...

随机推荐

  1. SpringMVC restful风格

    1.Spring对REST的支持 Spring3(这里讨论Spring3.2+)对Spring MVC的一些增强功能为REST提供了良好的支持.Spring对开发REST资源提供以下支持: 操作方式: ...

  2. nodejs卸载安装

    作为新手nodejs卸载后安装就总出错,今天记录了详细的步骤: 参考一下网址:写的很详细https://jingyan.baidu.com/article/48b37f8dd141b41a646488 ...

  3. js删除数组里指定的元素

    js删除数组里指定的元素 首先可以给JS的数组对象定义一个函数,用于查找指定的元素在数组中的位置,即索引,代码为: Array.prototype.indexOf = function(val) { ...

  4. MVC中的验证码

    下面是一个完整的mvc controller类 public class CodeController : Controller { private const string CODE = " ...

  5. CSS如何居中元素

    How to center in CSS 一步步拆解你的需求,是水平居中还是垂直居中?还是水平垂直居中?父容器是inline还是block,高度知不知,宽度造不造?一个子元素还是多个子元素?一行还是多 ...

  6. arcgis Flex QueryTask

    <esri:Map id="myMap" creationComplete="useMapServicePermaLink()" load="u ...

  7. 然之协同系统3.5(OA+CRM+CASH+TEAM)

    平台: Ubuntu 类型: 虚拟机镜像 软件包: mariadb-server 10.0.25 nginx 1.10.0 php7.0.4 collaboration commercial crm ...

  8. 笨办法学Python(八)

    习题 8: 打印,打印 formatter = "%r %r %r %r" print formatter % (1, 2, 3, 4) print formatter % (&q ...

  9. java Date equals 的坑

    今天在JDK6上做开发,遇到一个很诡异的问题. Domain中一个实体是Date,称为变量 a, 使用Calendar构造出来的Date,称为变量b, 虽然都是同一天,比如 2016-11-11 00 ...

  10. 2016 Multi-University Training Contest 4 - 1005 (hdu5768)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5768 题目大意:给你区间[L,R],问你[L, R]中有多少个数字x满足x%7=0且x%p[i]≠a[ ...