字符串类(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. C++ Primer笔记9_构造函数_拷贝构造(深拷贝与浅拷贝)

    1.构造函数: >构造函数是一个特殊的.与类同名的成员函数,用于给每一个成员设置适当的初始值. >构造函数不能有返回值,函数名与类名同样. >缺省构造函数时,系统将自己主动调用该缺省 ...

  2. Ubuntu12.04下jamvm1.5.4+classpath-0.98成功执行 helloworld.class

    经过两天的努力,总于在ubuntu以下编译好classpath-0.98与jamvm1.5.4,并能成功的运行类文件:jamvm hellowold,当屏幕上打印出"hello world! ...

  3. Result Cache结果高速缓存 (转)

    1.1 概述 SQL 查询结果高速缓存可在数据库内存中对查询结果集和查询碎片启用显式高速缓存.存储在共享池(Share Pool)中的专用内存缓冲区可用于存储和检索高速缓存的结果.对查询访问的数据库对 ...

  4. csss3 2D转换

    CSS3 转换 通过 CSS3 转换,我们能够对元素进行移动.缩放.转动.拉长或拉伸. 它如何工作? 转换是使元素改变形状.尺寸和位置的一种效果. 您可以使用 2D 或 3D 转换来转换您的元素. 浏 ...

  5. Jquery二级简单折叠菜单

    写在前面: 1.前端新手 2.欢迎交流 3. 源代码百度云页面示例链接: http://pan.baidu.com/s/1nt0yjd3 链接失效请留言 效果图: 代码部分:jquery部分: < ...

  6. C#根据函数名称执行对应的函数

    using System; using System.Collections.Generic; using System.Reflection; namespace test { public cla ...

  7. WebView的基本使用

    ---恢复内容开始--- 一.实例化WebView 通过xml实例化 xml <WebView android:id="@+id/webview" android:layou ...

  8. 百度地图SDk 使用

    第一步: 获取密钥 应用名称可以随便填写 发布版SHA1  的获取要在 keytool -v -list -keystore: C:\Users\admin\.android\debug.keysto ...

  9. SQL Server 创建作业系列问题

    一.从IClassFactory为CLSID为{AA40D1D6-CAEF-4A56-B9BB-D0D3DC976BA2}的COM组件创建实例失败. 尊重原著作:本文转载自http://www.hao ...

  10. Ubuntu 使用wget 命令下载JDK

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