从operator=中“自我赋值”看new的抛出异常

本文探讨了C++中自我赋值的问题,特别是当对象内部使用动态分配时可能遇到的问题。通过示例展示了传统的"证同测试"和优化后的赋值顺序以避免错误。同时,讨论了new操作在分配失败时会抛出异常而非返回空指针的情况,提出通过捕获异常或抑制new异常的方法来处理这种情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Effective C++中看到这个问题时才发现以前写的代码完全没有注意过这个问题

“自我赋值”发生在对象被赋值给对象本身时,例如:

class Weight
{...};
...
w=w;

虽然这看上去确实非常的蠢,但是谁也不能保证这不会发生,毕竟这是合法的,但是问题出来了,我们写的显示赋值函数一般是这样的,假设我们在类中使用了动态分配。

class Bitmap{...};
class Weight
{
...
private:
Bitmap * _pb;
};
Weight & Weight::operatot=(const Weight & rhs)
{
delete _pb;
_pb=new Bitmap(rhs._pb);
return *this;
}

好的,现在面临的问题是,*this和rhs这两个指向了同一个对象,在执行delete _pb 时同时也将rhs的Bitmap销毁了。

对此提出了两种有效的解决方法

①传统做法在operator=之前做一个“证同测试(identity test)”

Weight & Weight::operatot=(const Weight & rhs)
{
if(this == &rhs)
    return *this;
delete _pb;
_pb=new Bitmap(rhs._pb);
return *this;
}

这样做完全行得通,但是为一件很少发生的事情花费额外的判断操作似乎不是很划算。

②依靠精心周到的语句顺序解决
调整最初的源代码

Weight & Weight::operatot=(const Weight & rhs)
{
Bitmap *pOrig=_pb;                    //记住原先的_pb
_pb=new Bitmap(rhs._pb);              //令_pb指向*_pb的一个副本
delete pOrig;                         //删除原先的_pb
return *this;
}

再调整顺序后,在重新new之后再摧毁原来的_pb,这样的操作具有很高的异常安全性。这里有一个问题,如果new失败了怎么办?

我相信所有人一定都写过这样一段代码

int *p=new int[5];
if(p==NULL)
    return -1;

当我们使用malloc/calloc分配内存时,检测返回值是否为”空指针”是一个良好的习惯,可惜的是new在默认状态下,分配失败并不会返回一个空指针,而是抛出(throw)一个异常!!

对此正确的操作有如下两个方法:

①捕获异常

try
{
    _pb=new Bitmap(rhs._pb);  
    ...                     //其他操作
}
catch(const bad_alloc& e )
{
    return -1;
}

②标准 C++ 亦提供了一个方法来抑制 new 抛出异常,而返回空指针

   int* p = new (std::nothrow) int; 
   // 这样如果 new 失败了,就不会抛出异常,而是返回空指针
   if ( p == 0 ) // 如此这般,这个判断就有意义了
       return -1;
   ...           // 其它代码
   delete p;
这是我INI解析器的代码 #ifndef _DESC_H_ #define _DESC_H_ #include <string> namespace INI { class Desc { public: Desc(); ~Desc(); Desc(Desc&& _Instance)noexcept; Desc(const Desc& _Instance) = delete; //禁止复制构造 Desc& operator=(const Desc& _Instance) = delete; //禁止复制赋值 Desc& operator=(Desc&& _Instance)noexcept; Desc& operator=(const char* _szDesc); const char* GetText()const; const unsigned int GetLength()const; void clear(); //清除注释内容 char* m_szDesc; //注释内容 unsigned int m_uiLength; //注释长度 }; } #endif // !_DESC_H_ #include "../inc/Desc.h" INI::Desc::Desc() : m_szDesc(nullptr), m_uiLength(0) { } INI::Desc::Desc(Desc&& _Instance)noexcept : m_szDesc(_Instance.m_szDesc), m_uiLength(_Instance.m_uiLength) { _Instance.m_szDesc = nullptr; //挖空源内容 _Instance.m_uiLength = 0; } INI::Desc& INI::Desc::operator=(Desc&& _Instance)noexcept { if (this != &_Instance) { if (m_szDesc) { delete[] m_szDesc; m_szDesc = nullptr; } m_szDesc = _Instance.m_szDesc; m_uiLength = _Instance.m_uiLength; _Instance.m_szDesc = nullptr; //挖空源内容 _Instance.m_uiLength = 0; } return *this; } INI::Desc& INI::Desc::operator=(const char* _szDesc) { if (m_szDesc) { delete[] m_szDesc; m_szDesc = nullptr; } m_uiLength = strlen(_szDesc); if (m_uiLength > 0) { unsigned size = (m_uiLength + 1 + 3) & ~3;//4byte对齐 m_szDesc = new char[size]; memcpy(m_szDesc, _szDesc, m_uiLength + 1); } return *this; } INI::Desc::~Desc() { clear(); } const char* INI::Desc::GetText()const { return m_szDesc; } const unsigned int INI::Desc::GetLength()const { return m_uiLength; } void INI::Desc::clear() { if (m_szDesc) { delete[] m_szDesc; m_szDesc = nullptr; } m_uiLength = 0; }
07-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值