C++ 自赋值与异常安全问题
https://harttle.land/2015/07/30/effective-cpp-11.html,这个讲的很不错!
1.存在的问题
因为cpp中有指针和引用,它们可以指向同一个变量,所以会存在自赋值的问题。
a[i] = a[j]; //@ 如果i和j有同样的值,这里就是一次自赋值 *px = *py; //@ 如果px和py指向相同的东西,这里就是一次自赋值
自赋值一般存在:自赋值安全和异常安全,两个问题,例如:
1.1自赋值安全
Widget& Widget::operator=(const Widget& rhs){ delete pb; // stop using current bitmap pb = new Bitmap(*rhs.pb); // start using a copy of rhs's bitmap return *this; // see Item 10 }
当是自赋值的时候,pb已经先被删除了,那么后面的new就会为空,这是未知的计算。
1.2 异常安全
异常安全是指当异常发生时:
- 1) 不会泄漏资源,
- 2) 也不会使系统处于不一致的状态。
通常有三个异常安全级别:基本保证、强烈保证、不抛异常(nothrow)保证。
Widget& Widget::operator=(const Widget& rhs){ if (this == &rhs) return *this; delete pb; // stop using current bitmap pb = new Bitmap(*rhs.pb); // start using a copy of rhs's bitmap return *this; // see Item 10 }
这个自赋值安全,但是没有异常安全,如果new处出现了异常,那么pb仍旧指向空。
2.解决办法
2.1 通过调整语句顺序
Widget& Widget::operator=(const Widget& rhs){ Bitmap *pOrig = pb; // remember original pb pb = new Bitmap(*rhs.pb); // make pb point to a copy of *pb delete pOrig; // delete the original pb return *this; }
这里用pO指向原来的动态空间,在new时如果失败了抛出bad alloc异常,那么pb仍然指向原来的空间,不会悬空。
通过一个中间变量来实现,而没有使用if的判断,因为可能会影响效率。
其实也可以这样:
HasPtr & operator=(const HasPtr& hp){ std::string * t=new std::string(*hp.ps); delete ps; ps=t; i=hp.i; return *this; }
主要就是申请一个临时变量来指向原来的空间或者是新申请的空间。//delete应该是不会出现异常的吧。
2.2 copy&swap
Widget& Widget::operator=(Widget rhs){ swap(rhs); // swap *this's data with return *this; // the copy's }
注意参数为值拷贝,在cpp459页有讲解,
总结:
- 判断两个地址是否相同
- 仔细地排列语句顺序
- Copy and Swap
自赋值存在的知识点差不多这些。
3.不在自赋值中的异常安全问题
转自:https://harttle.land/2015/08/27/effective-cpp-29.html
但其实异常安全问题不仅仅存在于自赋值中,上述连接中给的例子,在更新数据成员时,也可能会出现异常安全的问题:
void Menu::changeBg(istream& src){
lock(&mutex);
delete bg;
++ changeCount;
bg = new Image(src);
unlock(&mutex);
}
当要更新bg时,new出现了问题,则mutex资源泄露,bg变为空悬指针。
针对这个问题,当然这个和自赋值问题是不同的,可以将数据成员bg用智能指针来管理,也可以使用copy&swap技术.
3.1 智能指针smart_ptr
class Menu{ shared_ptr<Image> bg; ... }; void Menu::changeBg(istream& src){ Lock m1(&m); bg.reset(new Image(src)); ++changeCont; }
reset函数中会delete掉原来的空间,但是如果new失败了,并不会进入reset函数,不会对bg产生影响。(但也有问题,但之后的叙述不明白。)
3.2 copy&swap
class Menu{ ... private: Mutex m; std::shared_ptr<MenuImpl> pImpl; }; Menu::changeBg(std::istream& src){ using std::swap; // 见 Item 25 Lock m1(&mutex); std::shared_ptr<MenuImpl> copy(new MenuImpl(*pImpl)); copy->bg.reset(new Image(src)); ++copy->changeCount; swap(pImpl, copy); }
先对原来的对象做一个拷贝,然后修改拷贝对象,再swap,函数结束时,copy对象及其指向的内存会被释放,不会造成泄露。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix