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 ...
随机推荐
- 牛客网Java刷题知识点之File对象常用功能:获取文件名称、获取文件路径、获取文件大小、获取文件修改时间、创建与删除、判断、重命名、查看系统根目录、容量获取、获取某个目录下内容、过滤器
不多说,直接上干货! 获取文件名称.获取文件路径.获取文件大小.获取文件修改时间 FileMethodDemo.java package zhouls.bigdata.DataFeatureSelec ...
- stm32 输入捕获学习(二)
(本文参考STM32 开发指南 V1.3 -- ALIENTEK 战舰 STM32 开发板库函数教程 ) 1. 实验设计 我们用 TIM5 的通道 1(PA0)来做输入捕获,捕获 PA0 上高电 ...
- Docker学习笔记(2)-docker镜像操作
本节将会涉及Docker的镜像操作. 1. 获取镜像 如何获取Docker Hub上的镜像?可通过docker pull命令获取,其格式为: docker pull [选项] [Docker Regi ...
- JQuery 技巧总结
一.简介 1.1.概述 随着WEB2.0及ajax思想在互联网上的快速发展传播,陆续出现了一些优秀的Js框架,其中比较著名的有Prototype.YUI.jQuery.mootools.Bindows ...
- Memcache安装、配置与学习
基于现在大多网站数据很多,由于页面性能问题我们都开始对站点使用缓存进行性能优化 Memcache扩展下载:http://windows.php.net/downloads/pecl/releases/ ...
- dapper.net框架使用随笔
一.简单介绍 Dapper是轻量级的ORM工具,代码就SqlMapper.cs一个文件,对于习惯使用原生的sql语句用户是个好选择,具有以下特性. 1.类似 ado.net 的写法,灵活拼接sql 2 ...
- php数组的定义
数组的主要作用就是存储数据,修改数据. 数组:就是把多个数据放在一起管理. 数组可以存入多个不同类型的数据,是一个复合数据类型. 在PHP中,有两种方式定义数组 //$array = array( ...
- 使用Ribbon Workbench来修改停用、激活按钮的权限
在实施的过程中,有时会遇到客户为了管控使用人员的操作或防止使用人员通过停用后再激活来绕开部分逻辑,需要对激活.停用按钮赋予单独的权限.但很遗憾,在Dyanmics CRM中,并没有把停用.激活按钮单独 ...
- ndk制作so库,ndk-build不是内部或外部命令。。。的错误
想了想大概就需要下面这几步: 1.下载ndk 2.配置ndk的环境变量 3.在android studio添加一些ndk的配置 4.编写c文件 5.生成so库 6.调用so库 上面提到的大部分问题你都 ...
- Web前端开发规范(二)
3.HTML代码规范 .html文件必须存放在项目工程约定的目录中. .html文件的命名:以模块 + 功能的结合方式来命名,比如:newsList.html. 文档类型声明:HTML4中使用< ...