前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >string类的实现

string类的实现

作者头像
Christal_R
修改2021-04-13 10:16:46
4630
修改2021-04-13 10:16:46
举报

string类底层是一个字符串指针

1、类结构定义

代码语言:javascript
复制
#include <iostream>
#include <cstring>
using namespace std;

class CMyString
{
private:
	char* m_pDate;
public:
	CMyString(const char* pDate = NULL); //普通构造函数,const:防止修改
	CMyString(const CMyString& other); //拷贝构造函数,const:防止修改,&:省去调用复制构造函数提高效率,涉及深拷贝、浅拷贝
	~CMyString(); //析构函数
	CMyString& operator = (const CMyString& other); //重构赋值运算符,返回引用:为了连续赋值,const:防止修改,&:省去调用复制构造函数提高效率,涉及安全性

	//CMyString& operator + (const CMyString& other);
	//bool operator == (const CMyString& other);
	int getLength();
	void printString(){ cout<<m_pDate<<endl; } //用于测试
};

2、简单说明

正如代码中注释部分说明,

const 是为了防止函数内部修改;

& 是为了省去隐式调用拷贝构造函数,从而提高效率;

3、详细说明

以“重构赋值运算符”例,详细解说注意事项

(1)是否把返回值的类型声明为该类型的引用,并在函数结束前返回实例自身的引用(即*this)。

只有返回一个引用,才可以允许连续赋值。否则如果函数的返回值是void,应用该赋值运算符将不能做连续赋值。假设有3个CMyString的对象:str1、str2和str3,在程序中语句str1=str2=str3将不能通过编译 。若只是两个对象之间的赋值,返回值为void也可以达到效果。

(2)是否把传入的参数的类型声明为常量引用。

如果传入的参数不是引用而是实例,那么从形参到实参会调用一次复制构造函数。把参数声明为引用可以避免这样的无谓消耗,能提高代码的效率。同时,我们在赋值运算符函数内不会改变传入的实例的状态,因此应该为传入的引用参数加上const关键字。即省去调用复制构造函数,提高效率。

(3)是否释放实例自身已有的内存。

如果我们忘记在分配新内存之前释放自身已有的空间,程序将出现内存泄露。

(4)是否判断传入的参数和当前的实例(*this)是不是同一个实例。

避免自赋值,如果是同一个,则不进行赋值操作,直接返回。如果事先不判断就进行赋值,那么在释放实例自身的内存的时候就会导致严重的问题:当*this和传入的参数是同一个实例时,那么一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此再也找不到需要赋值的内容了。即存在非法访问或者多次释放同一内存单元的风险。

(5)是否有申请内存失败的安全处理。

如果此时内存不足导致new char抛出异常,m_pData将是一个空指针,这样非常容易导致程序崩溃。先创建一个临时实例,再交换临时实例和原来的实例。把strTemp.m_pData和实例自身的m_pData做交换。由于strTemp是一个局部变量,但程序运行到 if 的外面时也就出了该变量的作用域,就会自动调用strTemp 的析构函数,把 strTemp.m_pData 所指向的内存释放掉。由于strTemp.m_pData指向的内存就是实例之前m_pData的内存,这就相当于自动调用析构函数释放实例的内存。即利用临时实例的生命周期自动释放原来实例内容。

4、类成员函数实现

(1)普通构造函数

参数为 const 防止修改

strlen计算字符串长度没有吧'\0'算进去,所以要+1

代码语言:javascript
复制
CMyString::CMyString(const char* pDate)
{
	if( pDate == NULL )
	{
		m_pDate = new char[1];
		*m_pDate = '\0';
	}
	else
	{
		//strlen计算字符串长度没有吧'\0'算进去
		m_pDate = new char[strlen(pDate)+1];
		strcpy(m_pDate, pDate);
	}
}

(2)拷贝构造函数

参数为 const 防止修改

参数加 & 省去调用赋值构造函数提高效率

(2.1)浅拷贝,也叫位拷贝

浅拷贝
浅拷贝
代码语言:javascript
复制
CMyString::CMyString( const CMyString& other ) //浅拷贝
{
	//没有重新申请新空间,共用同一块内存空间
	//隐患:非法访问,重复释放内存
	m_pDate = other.m_pDate;
}

浅拷贝引发的错误

浅拷贝引发的错误
浅拷贝引发的错误

(2.2)深拷贝

深拷贝
深拷贝
代码语言:javascript
复制
CMyString::CMyString( const CMyString& other ) //深拷贝
{
	//delete m_pDate;//既然也是属于构造函数的一类,初始为空,不必delete
	if( other.m_pDate == NULL )
	{
		m_pDate = NULL;
	}
	else
	{
		m_pDate = new char[strlen(other.m_pDate)+1];
		strcpy(m_pDate, other.m_pDate);
	}
}

(3)析构函数

释放前判断,避免重复释放

代码语言:javascript
复制
CMyString::~CMyString()
{
	if(m_pDate) //释放前判断,避免重复释放
	{
		delete m_pDate;
		m_pDate = NULL;
	}
}

(4)重构赋值运算符

返回引用 实现连续赋值

参数为 const 防止修改

参数加 & 省去调用赋值构造函数提高效率

(4.1)不安全实现

代码语言:javascript
复制
CMyString& CMyString::operator = ( const CMyString& other )
{
	if( &other != this ) //避免自赋值
	{
		if( m_pDate ) //先判断再删除,避免重复操作
			delete m_pDate;
		m_pDate = new char[strlen(other.m_pDate)+1]; //如果申请失败,后面strcpy会不安全
		strcpy(m_pDate, other.m_pDate);
	}
	return *this;
}

(4.2)安全实现

利用临时实例巧妙实现安全转移

代码语言:javascript
复制
CMyString& CMyString::operator = ( const CMyString& other )
{
	if( &other != this ) //避免自赋值
	{
		CMyString tmpOther(other);
		//让tmpOther跟this交换date
		char *tmpDate = tmpOther.m_pDate;
		tmpOther.m_pDate = m_pDate;
		m_pDate = tmpDate;

		//临时实例tmpOther退出if会自动调用析构函数,清除了原本m_pDate的内容
	}
	return *this;
}

5、输出实例

代码语言:javascript
复制
int main()
{
	CMyString str("hello"); //等同于 const char* p = "hello"; CMyString str(p);
	str.printString();

	cout<<"拷贝构造函数"<<endl;
	CMyString str1(str);
	str1.printString();

	cout<<"重载赋值操作符"<<endl;
	CMyString str2("world");
	str2.printString();

	CMyString str3("Birthday");
	str3.printString();

	str1 = str2 = str3;
	str1.printString();
	str2.printString();
	str3.printString();

	cout<<str1.getLength()<<endl;

	return 0;
}

输出样子:

输出样子
输出样子

6、参考

《后台开发》核心技术与应用实践

《剑指Offer》

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-04-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、类结构定义
  • 2、简单说明
  • 3、详细说明
    • (1)是否把返回值的类型声明为该类型的引用,并在函数结束前返回实例自身的引用(即*this)。
      • (2)是否把传入的参数的类型声明为常量引用。
        • (3)是否释放实例自身已有的内存。
          • (4)是否判断传入的参数和当前的实例(*this)是不是同一个实例。
            • (5)是否有申请内存失败的安全处理。
            • 4、类成员函数实现
              • (1)普通构造函数
                • (2)拷贝构造函数
                  • (3)析构函数
                    • (4)重构赋值运算符
                    • 5、输出实例
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档