赋值运算符函数

赋值运算符函数作为类的一个成员函数,主要用于对象之间的赋值。类一般都有默认的赋值运算符函数,然而默认赋值运算符函数只会浅拷贝,无法满足需求,有时还会产生致命错误。

如下:

class CString
{
public:
	CString()
		: _buf(nullptr)
	{
	}
    
    CString(const CString& str)
        : _buf(nullptr)
	{
		const int size = strlen(str._buf) + 1;
		_buf = new char[size];
		memset(_buf, 0, size);
		strcpy(_buf, str._buf);
	}

	CString(const char* str)
        : _buf(nullptr)
	{
		const int size = strlen(str) + 1;
		_buf = new char[size];
		memset(_buf, 0, size);
		strcpy(_buf, str);
	}

	~CString()
	{
		if (_buf != nullptr)
		{
			delete _buf;
			_buf = nullptr;
		}
	}

private:
	char* _buf;
};

int main()
{
	{
		CString str1("hello world!");
		CString str2;
		str2 = str1;
	}
}

如上代码所示,如果使用默认赋值运算符函数,str2 = str1;str2和str1将会指向同一块内存地址,当离开代码块作用域之后,str1和str2都将析构,这里将产生double free 内存错误。

对于这种需要深拷贝的赋值运算符函数需要自己定义。

下面是针对CString类的几种赋值运算符函数的实现:

CString& CString::operator=(const CString& str)
{
    if(this == &str)
    {
        return *this;
    }
    
    delete [] _buf;
    _buf = nullptr;
    
    const int size = strlen(str._buf) + 1;
    _buf = new char[strlen(str._buf) + 1];
    memset(_buf, 0, size);
    strcpy(_buf, str._buf);
    return *this;
}

如上赋值运算符返回值定义成引用类型,方便连续赋值,如str1 = str2 = str3;

赋值运算符参数定义成对象的常量引用,主要保证不会修改被赋值的对象;

实现中,先判断是否是对象自己,如果是则返回自己,

接着释放_buf内存,并置空

最后申请新的内存,将参数str中_buf数据拷贝到当前对象的buf中。

以上实现就是一个深拷贝。但是严格来讲也是存在问题的,当new char 抛出异常时,由于在这之前已经delete [] _buf,则当前对象就不能在保持有效状态,这就违背了异常安全性原则。好的做法应该是先申请内存,再释放内存,如下:

CString& CString::operator=(const CString& str)
{
    if(this == &str)
    {
        return *this;
    }
    
    const int size = strlen(str._buf) + 1;
    char* buf = new char[strlen(str._buf) + 1];
    memset(_buf, 0, size);
    
    delete [] _buf;
    _buf = buf;
    
    strcpy(_buf, str._buf);
    return *this;
}

还有另一种更优雅的实现:

CString& CString::operator=(const CString& str)
{
    if(this == &str)
    {
        return *this;
    }
    
    CString strTemp(str);
    char* buf = strTemp._buf;
    strTemp._buf = _buf;
    _buf = buf;
    return *this;
}

这种实现将new出现异常的情况转移到拷贝构造函数中,即便出现异常也不会影响当前对象的状态。

一般我们自己定义的类都需要实现赋值运算符函数,特别是含有动态内存的对象。赋值运算符函数要保证对象的赋值是深拷贝,而且在实现的过程中也要注意异常安全性,这样才能能写出健壮而优雅的代码。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值