字符串类(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. poj2488 A Knight's Journey

      A Knight's Journey Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 24840   Accepted:  ...

  2. 【Android Training UI】创建自定义Views(Lesson 0 - 章节概览)

    发表在我的独立网站http://kesenhoo.github.io/blog/2013/06/30/android-training-ui-creating-custom-views-lesson- ...

  3. Swift的初始化方法

    我们在深入初始化方法之前,不妨先再想想Swift中的初始化想要达到一种怎样的目的. 其实就是安全.在Objective-C中,init方法是非常不安全的:没有人能保证init只被调用一次,也没有人保证 ...

  4. 【ThinkPHP学习】ThinkPHP自己主动转义存储富文本编辑器内容导致读取出错

    RT. ThinkPHP的conf文件里的Convention.php有一个配置选项 'DEFAULT_FILTER' => 'htmlspecialchars', // 默认參数过滤方法 用于 ...

  5. Flash Recovery Area 的备份

    Flash Recovery Area 的备份 备份命令是Flash recovery Area,该命令是Oracle 10g以后才有的.10g引进了flash recovery area,同时在rm ...

  6. 异步消息处理机制——Handler用法

    Handler 1. Message Messsge是线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据,Message的what字段,除此之外还可以使用arg1和arg2字段 ...

  7. juce 中的WeakReference分析

    juce中的WeakReference设计得比较巧妙,巧妙就是使用delete之后就可以通知道WeakReference,原理其实也很间单,其实就是在对象里添加了一个子对象masterReferenc ...

  8. ZRender源码分析4:Painter(View层)-中

    回顾 上一篇说到:ZRender源码分析3:Painter(View层)-上,接上篇,开始Shape对象 总体理解 先回到上次的Painter的render方法 /** * 首次绘图,创建各种dom和 ...

  9. Android_神奇的android:clipChildren属性

    正文 一.效果图 看到这个图时你可以先想想如果是你,你怎么实现这个效果.马上想到用RelativeLayout?NO,NO,NO,,, 二.实现代码 <?xml version="1. ...

  10. MySQL Workbench是一款专为MySQL设计的ER/数据库建模工具

      MySQL  Workbench是一款专为MySQL设计的ER/数据库建模工具.它是著名的数据库设计工具DBDesigner4的继任者.你可以用MySQL  Workbench设计和创建新的数据库 ...