字符串类(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. Zipper

      Zipper Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  2. android开发 不注意的异常

    近期刚上班,做了一个应用,要上线.測试天天測试,天天有bug,个人总结了几个常常忽略的地方: 1:在 继承 BaseAdapter 的 @Override public int getCount() ...

  3. Struts2 实现文件上传

    单个文件上传 关于如何创建Struts2项目:Struts2 初体验. 一.创建jsp页面: 注意!要上传文件,表单必须添加 enctype 属性,如下:  enctype="multipa ...

  4. poj3461Oulipo

    Description The French author Georges Perec (1936–1982) once wrote a book, La disparition, without t ...

  5. Bootstrap插件——(Tab)标签页

    项目中用到了Bootstrap的(Tab)标签页插件,记录如下: 代码如下: <div class="tabbable"> <ul class="nav ...

  6. HDU 1020 Encoding POJ 3438 Look and Say

    Encoding Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  7. Ubuntu 使用wget 命令下载JDK

    wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-co ...

  8. 关于 table-layout 属性

    我们经常会使用table,并且去手动的设置它的宽度,比如: <table> <tr> <td class="left">...</td&g ...

  9. mysqli扩展库的 预处理技术 mysqli stmt

    问题的提出? 现在需要向mysql数据库添加100个用户,请问如何实现? 思路: 使用for循环100次,向数据库中添加100个用户. 使用批量添加 $sql1=”insert xxx”; $ssql ...

  10. php安全编程—sql注入攻击

    php安全编程--sql注入攻击 定义 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因 ...