字符串类(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. public void Delete<T>(List<T> EntityList) where T : class, new() 这是什么意思

    就是说T必须是一个类(class)类型,不能是结构(structure)类型. 这是类型参数约束,.NET支持的类型参数约束有以下五种: where T : struct                ...

  2. 黑马程序员_<<Set,HashSet>>

    --------------------ASP.Net+Android+IOS开发..Net培训.期待与您交流! -------------------- 1.Set Set是Collection接口 ...

  3. 启动监听报错:TNS-12537: TNS:connection closed TNS-12560: TNS:protocol adapter error TNS-00507: Connection closed Linux Error: 29: Illegal seek

    启动监听程序报错: 说明:在rhel5.8上安装完成oracle11g数据库后,使用netca创建完监听,启动监听时报错.还未使用dbca创建实例. [oracle@rusky-oracle11g ~ ...

  4. asp.net中的绝对路径和相对路径

    一.关于相对路径和绝对路径相对路径转绝对路径一般,我们在ASP.NET网站中往往需要把一个相对路径转化为绝对路径.通常是用Server.MapPath()方法.比如网站根目录下有 个"Upl ...

  5. js 多媒体audio video

    本文主要简单的介绍一下audio 和 video两个标签的用法 <audio src="music.mp3"></audio> <video src= ...

  6. 移动端影像解决方案Adobe Creative SDK for ios

    移动端影像解决方案Adobe Creative SDK for ios 2015-12-20 分类:整理 阅读(390) 评论(0)  老牌影像界泰斗不甘落寞,正式推出了Adobe Creative ...

  7. jQuery :lt()选择器

    定义和用法 :lt() 选择器选取 index 值小于指定数字的元素.(不包含指定值) index 值从 0 开始.(从0开始计数) 最常见的用法:与其他选择器一起使用,选取指定组合中特定序号之前的元 ...

  8. oracle11g用户名密码不区分大小写

    oracle 11g 以前的版本的用户名和密码是不区分大小写的; oracle 11g 用户名和密码默认区分大小写,可更改alter system set sec_case_sensitive_log ...

  9. zoj1108 FatMouse's Speed

    给你每个物体两个参数,求最长的链要求第一个参数递增,第二个参数递减,要求输出任意最长路径. 首先第一反应根据第二个参数排个序,然后不就是最长上升子序列的问题吗? O(nlogn)的复杂度,当然这样可以 ...

  10. Web页面在手机上显示过大问题

    网上抄来了,自己也备忘下:增加<meta name="viewport" content="width=device-width, initial-scale=1. ...