通过自定义string类型来理解运算符重载
Posted Redamanc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过自定义string类型来理解运算符重载相关的知识,希望对你有一定的参考价值。
自定义string类型
理解系统自带string类型
在C语言中我们一般使用字符数组来保存字符串。
存在诸多不方便的地方:比如数组大小、数组长度、数组扩容等等。
为了更好地使用字符串,C++提供了string
类型。
使用它需要引入头文件:#include <string>
。
示例代码
我们可以通过如下代码,来简单展示系统提供string
类型的功能:
#include <iostream>
#include <string>
using namespace std;
int main()
String str1;
String str2 = "aaa";
String str3 = "bbb";
String str4 = str2 + str3;
String str5 = str2 + "ccc";
String str6 = "ddd" + str2;
cout << "str6:" << str6 << endl;
if (str5 > str6)
cout << str5 << " > " << str6 << endl;
else
cout << str5 << " < " << str6 << endl;
int len = str6.length();
for (int i = 0; i < len; i++)
cout << str6[i] << " ";
cout << endl;
char buff[1024] = 0 ;
strcpy(buff, str6.c_str());
cout << "buff:" << buff << endl;
return 0;
功能展示
运行上述代码,得到结果如下:
我们可以看到:系统提供的string
类型支持以下操作:
- 支持两个对象的相加;
- 支持对象和常量字符串的相加;
- 支持对象之间的比较操作(通过字典比较大小);
- 支持输出运算符的重载;
- 支持下标运算符的重载。
注意:
这里列出的操作仅是代表本示例代码体现出的功能,并不代表完整的功能。
自定义string类型
为了和系统提供的string
类型做出区分,我们自定义的类型取名为:String
(大写了首字母)。
由于我们使用到了其他地方的资源(一般是指堆上),故系统缺省的拷贝构造函数(浅拷贝)会出问题,所以我们需要自定义拷贝构造函数,同理,赋值函数也需要自定义。
构造、析构、拷贝构造、赋值
函数自定义如下:
// 自己实现一个字符串对象
class String
public:
String(const char* p = nullptr)
if (p != nullptr)
_pstr = new char[strlen(p) + 1];
strcpy(_pstr, p);
else
_pstr = new char[1];
*_pstr = '\\0';
~String()
delete[] _pstr;
_pstr = nullptr;
String(const String& str)
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
String& operator=(const String& src)
if (this == &src)
return *this;
delete[]_pstr;
_pstr = new char[strlen(src._pstr) + 1];
strcpy(_pstr, src._pstr);
return *this;
private:
char* _pstr;
;
比较运算符(>、<、==)重载
通过系统提供的string
类型实现的功能我们可以看到,比较运算符(>、<、==)是返回一个bool
值,根据两个字符串的字典顺序比较大小。
具体实现如下:
bool operator>(const String& str)const
return strcmp(_pstr, str._pstr) > 0;
bool operator<(const String& str)const
return strcmp(_pstr, str._pstr) < 0;
bool operator==(const String& str)const
return strcmp(_pstr, str._pstr) == 0;
细节:
因为只涉及读操作,故我们均把它实现为常方法(const)
,并且传递进来的参数也只涉及读操作,并且设计为引用
。
输出运算符(<<)重载
一般而言,对于自定义的类型,系统编译器不知道输出运算符需要输出什么信息,需要提供运算符(<<)的重载。
并且由于该运算符的调用不依赖于特定的对象,故我们设计为全局函数
,这么一来,就无法访问到类型的私有数据(private)
,针对此问题,我们可以将该重载方法定义为该类型的友元函数
。
具体实现如下:
class
public:
private:
friend ostream& operator<<(ostream& out, const String& str);
;
ostream& operator<<(ostream& out, const String& str)
out << str._pstr;
return out;
加法运算符(+)重载
对于string
类型来说,+
意味着将两个字符串进行合并
。
可能我们首先会想到strcat()
函数,将两个字符串连接起来。
这里存在的一个问题就是:合并后的字符串是否有足够的空间容纳
,我们会发现,前面的相关构造函数
都是通过strlen()
方法计算实参的大小,并以此开辟空间大小,换句话说:有多大开辟多大,一点不剩
。所以,将后一个字符串连接到前一个字符串的时候,就会出现空间不够的问题。
如果意识到这个问题,那么实现就很容易了。
具体实现如下:
String operator+(const String& lhs, const String& rhs)
char* _ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
strcpy(_ptmp, lhs._pstr);
strcat(_ptmp, rhs._pstr);
String tmp(_ptmp);
delete[]_ptmp;
return tmp;
注意:
这里的实现依然实现在全局函数
中,故,为了能够访问私有数据,依然要定义为友元函数。
下标运算符([])重载
通过下标运算符,可以模拟实现数组下标的相关操作。
具体实现如下:
char& operator[](int index) return _pstr[index];
完整代码
#include <iostream>
#include <string>
#pragma warning(disable:4996)
using namespace std;
// char arr[]="jkasdas";
// 自己实现一个字符串对象
class String
public:
String(const char* p = nullptr)
if (p != nullptr)
_pstr = new char[strlen(p) + 1];
strcpy(_pstr, p);
else
_pstr = new char[1];
*_pstr = '\\0';
~String()
delete[] _pstr;
_pstr = nullptr;
String(const String& str)
_pstr = new char[strlen(str._pstr) + 1];
strcpy(_pstr, str._pstr);
String& operator=(const String& src)
if (this == &src)
return *this;
delete[]_pstr;
_pstr = new char[strlen(src._pstr) + 1];
strcpy(_pstr, src._pstr);
return *this;
bool operator>(const String& str)const
return strcmp(_pstr, str._pstr) > 0;
bool operator<(const String& str)const
return strcmp(_pstr, str._pstr) < 0;
bool operator==(const String& str)const
return strcmp(_pstr, str._pstr) == 0;
int length()const return strlen(_pstr);
char& operator[](int index) return _pstr[index];
const char& operator[](int index)const return _pstr[index];
const char* c_str()const return _pstr;
private:
char* _pstr;
friend String operator+(const String& lhs, const String& rhs);
friend ostream& operator<<(ostream& out, const String& str);
;
ostream& operator<<(ostream& out, const String& str)
out << str._pstr;
return out;
String operator+(const String& lhs, const String& rhs)
char* _ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
strcpy(_ptmp, lhs._pstr);
strcat(_ptmp, rhs._pstr);
String tmp(_ptmp);
delete[]_ptmp;
return tmp;
功能对比
我们此时将示例代码中的所有string
转换成String
,来对比看看结果:
可以看到,一模一样
。
存在问题
但是我们自定义的代码并不是完美的,它存在这样的问题:
在加法运算符重载函数
中:
我们的代码如下:
String operator+(const String& lhs, const String& rhs)
char* _ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
strcpy(_ptmp, lhs._pstr);
strcat(_ptmp, rhs._pstr);
String tmp(_ptmp);
delete[]_ptmp;
return tmp;
乍一看好像没有什么问题,但是我们仔细研究,就会发现,
为了将局部新开辟的内存空间_ptmp
资源释放,我们后面又构造了一个临时对象String tmp(_ptmp)
,之后将局部变量资源释放delete[]_ptmp
,但是这样一来的效率非常低!
那么如何能够实现不泄露内存
,并且提高效率
呢?
未完待续。
以上是关于通过自定义string类型来理解运算符重载的主要内容,如果未能解决你的问题,请参考以下文章