MyString类的实现--基础中的基础C语言
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语言的更多相关文章
- [.net 面向对象编程基础] (3) 基础中的基础——数据类型
[.net 面向对象编程基础] (3) 基础中的基础——数据类型 关于数据类型,这是基础中的基础. 基础..基础..基础.基本功必须要扎实. 首先,从使用电脑开始,再到编程,电脑要存储数据,就要按类型 ...
- [.net 面向对象编程基础] (4) 基础中的基础——数据类型转换
[.net面向对象编程基础] (4)基础中的基础——数据类型转换 1.为什么要进行数据转换? 首先,为什么要进行数据转换,拿值类型例子说明一下, 比如:我们要把23角零钱,换成2.30元,就需要把整形 ...
- [.net 面向对象编程基础] (5) 基础中的基础——变量和常量
[.net面向对象编程基础] (5) 基础中的基础——变量和常量 1.常量:在编译时其值能够确定,并且程序运行过程中值不发生变化的量. 通俗来说,就是定义一个不能改变值的量.既然不能变动值,那就必须 ...
- [.net 面向对象编程基础] (7) 基础中的基础——流程控制语句
[.net 面向对象编程基础] (7) 基础中的基础——流程控制语句 本来没有这一节的内容,后来考虑到既然是一个系列文章,那么就尽可能写的详细一些,本节参考了网上朋友所写的例子,为的是让更多小伙伴学习 ...
- [.net 面向对象编程基础] (8) 基础中的基础——修饰符
[.net 面向对象编程基础] (8) 基础中的基础——修饰符 在进入C#面向对象核心之前,我们需要先对修饰符有所了解,其实我们在前面说到变量和常量的时候,已经使用了修饰符,并且说明了变量和常量的修改 ...
- [.net 面向对象编程基础] (6) 基础中的基础——运算符和表达式
[.net 面向对象编程基础] (6) 基础中的基础——运算符和表达式 说起C#运算符和表达式,小伙伴们肯定以为很简单,其实要用好表达式,不是一件容易的事.一个好的表达式可以让你做事半功倍的效果,比如 ...
- 从头开始学JavaScript 笔记(一)——基础中的基础
原文:从头开始学JavaScript 笔记(一)--基础中的基础 概要:javascript的组成. 各个组成部分的作用 . 一.javascript的组成 javascript ECMASc ...
- C++ 基础中的基础 ---- 引用
C++ 基础中的基础 ---- 引用 引用的概念:引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字.一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量.比如: int n ...
- python之基础中的基础(一)
python是一个效率极高的语言,现在市面上的机器学习大部分是由python和R语言完成,所以在不久之前小仙心中便种下了学习python的想法.下面是这一个月多月以来学习的总结,都是基础中基础了. 1 ...
随机推荐
- linux_api之文件操作
本篇索引: 1.引言 2.文件描述符 3.open函数 4.close函数 5.read函数 6.write函数 7.lseek函数 8.i/o效率问题 9.内核用以维护打开文件的相关数据结构 10. ...
- 类模版的static成员
类模版中声明static成员 template <class T> class Foo { public: static size_t count() { ++ctr; cout < ...
- Xen安装部署和基本原理
Refer to : http://www.178linux.com/14764 目录: 1. Xen的简介 1.1 Xen的大体结构 1.2 Xen对VM的称呼 1.3 Xen对C ...
- DataGridView带图标的单元格实现
目的: 扩展 C# WinForm 自带的表格控件,使其可以自动判断数据的上下界限值,并标识溢出. 这里使用的方法是:扩展 表格的列 对象:DataGridViewColumn. 1.创建类:Data ...
- c#-day02学习笔记
类型转化 为什么要类型转化:因为C#语言是强类型的语言,所以区分了很多的类型,类型和类型之间是不能直接赋值的,如果要赋值 就需要转换类型 类型转换分为两大类: 第一类:隐式转换 隐式转换是系统默认的转 ...
- Xtrareport绘制行号
需要是用事件beforePrint (在打印数据之前的事件) private void xrTableCell12_BeforePrint(object sender, System.Drawing. ...
- 内表转WORD
组合HTML字符串的方法来导出WORD文件 DATA: BEGIN OF wa_html, zhtml(), END OF wa_html, gt_html LIKE TABLE OF wa_html ...
- CSS实现多重边框和内凹圆角
CSS实现多重边框 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset=&q ...
- Android Studio 导入 AOSP 源码
有了 AOSP 源码,接下来就是如何看了,可以直接文本看,可以用 Source Insight,我当然选择 Android Studio,Android Studio 是我熟悉且十分强大的工具.问题来 ...
- python数据类型和数据运算
数字 整型 包括正整数和负整数,和数学的表示方法一样.如:1.100.8008.-12等. 浮点型 浮点数字也称为小数,如果按照科学计数法表示时,小数点的位置是可变的.如:1.23x109==12.3 ...