字符串类(String),熟悉内存管理与拷贝控制

    • 类定义
#include <iostream>
//#include <cstring>
using std::cout;
using std::cin; class String{
using iterator = char *;
friend std::ostream &operator<< (std::ostream &, const String &);
friend std::istream &operator>> (std::istream &, String &);
friend String operator + (const String &, const String &);
friend String operator + (const String &, const char *); //对于非类内的函数,必须参数中存在类类型的参数;
friend String operator + (const char *, const String &); //否则会与默认类型的操作符冲突
friend unsigned int getline(std::istream &, String &);
public:
String(const char *rhs = "hello, world"); //构造函数,默认为"hello, world" 通常我们可以默认为NULL
String(const String &); //拷贝构造函数
String &operator= (const String&); //拷贝赋值运算符
String &operator= (const char *); //拷贝赋值运算符
String & operator += (const String&);
String & operator += (const char *);
char operator[] (int);
iterator begin();
iterator end();
String & push_back(const char);
String & clear(); ~String() { delete [] data; } //析构函数
private:
char *data;
};
std::ostream & operator<< (std::ostream &, const String &);
std::istream &operator>> (std::istream &, String &); inline String::String(const char *rhs)
{
if (!rhs) //必须先检查rhs是否为NULL;对于NULL的拷贝,会去读取未知地址
data = new char[]{}; //因为我们的析构函数调用了delete,所以必须开辟一个空间,
//否则代码在调用析构函数时会delete一个错误空间
else {
data = new char[strlen(rhs) + ];
strcpy(data, rhs);
}
} inline String& String::operator= (const String& rhs) //拷贝赋值运算
{
this->data = new char[strlen(rhs.data) + ];
strcpy(this->data, rhs.data);
return *this;
} inline String & String::operator= (const char *rhs) //拷贝赋值运算符
{
this->data = new char[strlen(rhs) + ];
strcpy(this->data, rhs);
return *this;
}
    • 类方法实现
#include "String.h"

inline String::String(const String &rhs)            //拷贝构造函数
{
data = new char[strlen(rhs.data) + ]; //每次都要 +1 的原因是strlen不包含最后的'\0'
strcpy(data, rhs.data);
} std::ostream &operator<< (std::ostream &os, const String &rhs)
{
os << rhs.data;
return os;
} std::istream& operator >> (std::istream &is, String &rhs)
{
is >> rhs.data;
return is;
} String & String::operator += (const String &rhs)
{
if (!rhs.data) //如果要加的类的数据为NULL,则不需要处理了
return *this;
else if (!data) //如果原来的数据为NULL,则直接把后来的数据拷贝过来;由于上面的判断,所以rhs.data != NULL;
{
this->data = new char[strlen(rhs.data) + ];
strcpy(this->data, rhs.data);
return *this;
}
else //data != NULL && rhs.data != NULL
{
char *tmp = new char[strlen(this->data) + ];
strcpy(tmp, this->data);
this->data = new char[strlen(data) + strlen(rhs.data) + ];
strcpy(data, tmp);
strcat(data, rhs.data);
free(tmp);
return *this;
}
} String & String::operator += (const char * rhs)
{
if (!rhs) //如果要加的类的数据为NULL,则不需要处理了
return *this;
else if (!data) //如果原来的数据为NULL,则直接把后来的数据拷贝过来;由于上面的判断,所以rhs.data != NULL;
{
this->data = new char[strlen(rhs) + ];
strcpy(this->data, rhs);
return *this;
}
else //data != NULL && rhs.data != NULL
{
char *tmp = new char[strlen(this->data) + ];
strcpy(tmp, this->data);
this->data = new char[strlen(data) + strlen(rhs) + ];
strcpy(data, tmp);
strcat(data, rhs);
free(tmp);
return *this;
}
} String operator + (const String &lhs, const String &rhs)
{
String tmp(lhs);
tmp += rhs;
return tmp;
} String operator + (const String &lhs, const char *rhs)
{
String tmp(lhs);
tmp += rhs;
return tmp;
} String operator + (const char *lhs, const String &rhs)
{
String tmp(lhs);
tmp += rhs;
return tmp;
} String::iterator String::begin()
{
return data;
} String::iterator String::end()
{
return data + strlen(data);
} char String::operator[] (int index)
{
if (index > strlen(data) - || index < )
{
cout << "index error";
return ;
}
else
return *(data + index);
} String & String::push_back(const char ch)
{
char *tmp = new char[strlen(data) + ];
strcpy(tmp, data);
*(tmp + strlen(data)) = ch; //data + strlen(data)是原来的'\0'处
*(tmp + strlen(data) + ) = '\0';
data = new char[strlen(data) + ];
strcpy(data, tmp);
//free(tmp);
delete[] tmp; //delete [] tmp = free(tmp) : 怀疑delete就是把free封装了
return *this;
} String & String::clear()
{
delete[] data;
data = new char[]{};
return *this;
}
unsigned int getline(std::istream &is, String & rhs)
{
char tmp;
rhs.clear();
while (is.get(tmp))
{
if (tmp && tmp != '\n')
rhs.push_back(tmp);
else
break;
}
return strlen(rhs.data);
}
  • 先把开始的写好了,后面可以一直重用它,比如 += 和 +;
  • 析构函数中需要使用delete [] data,是因为很多操作中都需要开辟空间,所以在默认构造函数中虽然里面不放东西但是还需要开辟一个空间,因为当这个类由于某些原因调用析构函数时,需要有一块空间给delete使用
  • 使用智能指针应该也是可以的,这样的话就不需要delete了

String类(C++练习二)的更多相关文章

  1. Java第二次作业--数组和String类

    Deadline: 2017-3-28 23:00 一.学习要点 认真看书并查阅相关资料,掌握以下内容: 掌握基本数据类型和引用数据类型的区别 理解对象的生成与引用的关系 掌握构造方法的重载 掌握St ...

  2. 一大波Java来袭(四)String类、StringBuilder类、StringBuffer类对照

    本文主要介绍String类.StringBuffer类.StringBuilder类的差别  : 一.概述 (一)String 字符串常量.可是它具有不可变性,就是一旦创建,对它进行的不论什么改动操作 ...

  3. 20155326 第12周课堂实践总结(二)String类和Arrays类的学习

    20155326 第12周课堂实践总结(二)String类和Arrays类的学习 实践二 Arrays和String单元测试 实践题目 在IDEA中以TDD的方式对String类和Arrays类进行学 ...

  4. Java基础——String类(二)

    今天做了几道String常见操作.先来几个代码实例: 例一:此方法,仅把字符串前后出现的空格去掉了,中间部分不会. class TestTrim { public static void main(S ...

  5. Java常用类(二)String类详解

    前言 在我们开发中经常会用到很多的常用的工具类,这里做一个总结.他们有很多的方法都是我们经常要用到的.所以我们一定要把它好好的掌握起来! 一.String简介 1.1.String(字符串常量)概述 ...

  6. string类(二、常用string函数)

    常用string相关,参至System.String类: 1/ string.Length a.Length字符串长度 string a="a5"; //a.Length==2 s ...

  7. java源码解析之String类(二)

    上一节主要介绍了String类的一些构造方法,主要分为四类 无参构造器:String(),创建一个空字符串"",区别于null字符串,""已经初始化,null并 ...

  8. JDK中String类的源码分析(二)

    1.startsWith(String prefix, int toffset)方法 包括startsWith(*),endsWith(*)方法,都是调用上述一个方法 public boolean s ...

  9. JAVASE(十二) Java常用类: 包装类、String类、StringBuffer类、时间日期API、其他类

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 1.包装类 1 .1 八个包装类 ​ 1. 2 基本数据类型,包装类,String者之间的转换 ​ 2. ...

  10. String类的构造方法详解

    package StringDemo; //String类的构造方法详解 //方法一:String(); //方法二:String(byte[] bytes) //方法三:String (byte[] ...

随机推荐

  1. ExtJs3学习资料分享

    最近在学习EXTJS3,在网上找了一些pdf的书.不过网上分享的有些书都是Ext2.0的.Ext3的比较少.有些书也不全.很多是样章.最近找到一本分享的书<ExtJS源码分析与开发实例宝典> ...

  2. 依赖注入及AOP简述(九)——单例和无状态Scope .

    三.依赖注入对象的Scope及其生命周期 在前面的章节我们讲到,依赖注入容器之所以能够区别于以往的ServiceLocator等容器,是在于其不但能够自动构建多层次的.完整的依赖关系图,并且可以管理依 ...

  3. ORACLE数据库存储结构简介(转)

    首先,oracle数据库的存储结构可以分为逻辑存储结构和物理存储结构,对于这两种存储结构,oracle是分别进行管理的.   逻辑存储结构:oracle内部的组织和管理数据的方式.  物理存储结构:o ...

  4. 基于Mesos运行Spark

    背景介绍 Spark有多种集群运行模式,例如:Standalone,Yarn,Mesos.      下面就说一下如何在Mesos上运行Spark,这也是官方推荐的一种运行方式.      在运行Sp ...

  5. DictoryInfo.GetFiles

    using System; using System.IO; namespace ConsoleApplication1 { class Program { static void Main(stri ...

  6. jQuery Tags Input Plugin 插件的使用

    一个jquery开发的标签功能加强插件,可以生成或删除标签,还能对输入重复标签进行检查,和JQuery autocomplete插件配合实现自动完成功能. 官网:http://xoxco.com/pr ...

  7. 根据树父子ID拼接无限极树结构表的名称

    declare @c varchar(50)set @c='572a3d51-ef7a-459e-a5cd-ebf0fca51e8b' --能查出来呀 你试试,我试一下,好像可以啦谢谢 declare ...

  8. JAVA Calendar详解(转)

    转自:http://blog.csdn.net/zerogotosum/article/details/1671314 (在文章的最后,将会介绍Date类,如果有兴趣,可以直接翻到最后去阅读) 究竟什 ...

  9. 各种Adapter的用法

    同样是一个ListView,可以用不同的Adapter让它显示出来,比如说最常用的ArrayAdapter,SimpleAdapter,SimpleCursorAdapter,以及重写BaseAdap ...

  10. flac文件提取专辑封面手记

    博客迁移后整理发型这篇文章当时没写完,不补了,不过还是得说明一些东西 下面这部分代码可用之处为从flac文件头开始然后各种形式的大跳,最后到达专辑封面的数据块,之后解析. 当时写的时候不会写图片解析部 ...