异常安全的复制构造函数和拷贝赋值运算符函数
来源:互联网 发布:网络基础知识有哪些 编辑:程序博客网 时间:2024/05/29 02:52
异常安全性问题
- 异常安全代码
在出现异常的情况下仍然能够正确运行- 异常中立
将所有的异常都转发给调用者- 永远不要在析构函数、重载运算符函数operator delete()或者operator delete[]中抛出异常
在编写析构函数和内存释放函数时,要假设存在throw()这样的异常规范
以template < typename T > class Stack为例
template <typename T>class Stack{public: Stack(); ~Stack(); Stack( const Stack & ); Stack & operator=( const Stack & ); size_t Count() const { return vused_; } void Push( const T& t ); T & Top() { if ( 0 == vused_ ) { throw "empty stack"; } return v_[vused_-1]; } void Pop() { if ( 0 == vused_ ) { throw "pop from empty stack"; } else { -- vused_; } }private: T *NewCopy( const T *src, size_t srcsize, size_t destsize ); T * v_; size_t vsize_; size_t vused_;};
默认构造过程
template <typename T>Stack<T>::Stack() : v_( new T[10] ), vsize_( 10 ), vused_( 0 ){}
析构过程
template <typename T>Stack<T>::~Stack(){ delete[] v_;}
辅助函数
template <typename T>T *Stack<T>::NewCopy( const T *src, size_t srcsize, size_t destsize ){ assert( destsize >= srcsize ); T *dest = new T[destsize]; try { copy( src, src+srcsize, dest ); } catch ( ... ) { delete[] dest; throw; } return dest;}
拷贝构造函数
template <typename T>Stack<T>::Stack( const Stack<T> &other ) : v_( NewCopy( other.v_, other.vsize_, other.vsize_ ) ), vsize_( other.vsize_ ), vused_( other.vused_ ){}
拷贝赋值过程
template <typename T>Stack<T> &Stack<T>::operator=( const Stack<T> &other ){ if ( this != &other ) { T *v_new = NewCopy( other.v_, other.vsize_, other.vsize_ ); delete[] v_; v_ = v_new; vsize_ = other.vsize_; vused_ = other.vused_; } return *this;}
Push()
template <typename T>void Stack<T>::Push( const T &t ){ if ( vused_ == vsize_ ) { size vsize_new = vsize_*2+1; T *v_new = NewCopy( v_, vsize_, vsize_ ); delete[] v_; v_ = v_new; vsize_ = vsize_new; } v_[vused_] = t; ++ vused_;}
- 基本保证:如果在类型T中或者程序中其他的地方抛出了异常,对象不应该造成资源泄漏
- 强保证:如果某个操作由于抛出异常而终止,那么程序的状态应该保持不变
- 无异常抛出保证:无论什么情况下,函数都不会抛出异常
封装内存管理工作
template <typename T1, typename T2>void construct( T1 *p, const T2 &value ){ new (p) T1(value);}template <typename T>void destroy( T *p ){ p->~T();}template <typename T>void destroy( T first, T last ){ while ( first != last ) { destroy( &*first ); ++ first; }}template <typename T>class StackImpl{protected: StackImpl( size_t size=0 ) : v_( static_cast<T *>( 0==size ? 0 : operator new( sizeof(T)*size ) ) ), vsize_( size ), vused_( 0 ) { } ~StackImpl() { destroy( v_, v_+vused_ ); operator delete( v_ ); } void Swap( StackImpl & other ) throw() { swap( v_, other.v_ ); swap( vsize_, other.vsize_ ); swap( vused_, other.vused_ ); } T *v_; size_t vsize_; size_t vused_;private: StackImpl( const StackImpl & ); StackImpl & operator=( const StackImpl & );};
更好的Stack
template <typename T>class Stack : private StackImpl<T>{public: Stack( size_t size=0 ) : StackImpl<T>( size ) { } ~Stack(); Stack( const Stack &other ) : StackImpl<T>( other.vused_ ) { while ( vused_ < other.vused_ ) { construct( v_+vused_, other.v_[vused_] ); ++ vused_; } } Stack & operator=( const Stack & other ) { Stack temp( other ); Swap( temp ); return *this; } size_t Count() const { return vused_; } void Push( const T & ) { if ( vused_ == vsize_ ) { Stack temp( vsize_*2+1 ); while ( temp.Count() < vused_ ) { temp.Push( v_[temp.Count()] ); } temp.Push( t ); swap( temp ); } else { construct( v_+vused_, t ); ++ vused_; } } T & Top() { if ( 0 == vused_ ) { throw "empty stack"; } return v_[ vused_-1 ]; } void Pop() { if ( 0 == vused_ ) { throw "pop from empty stack"; } else { -- vused_; destroy( v_+vused_ ); } }}
优雅的实现
假设在StackImpl中,属性为public
template <typename T>class Stack{public: Stack( size_t size=0 ) : impl( size ) { } ~Stack(); Stack( const Stack & other ) : impl( other.impl_.vused_ ) { while ( impl_.vused_ < other.impl_.vused_ ) { construct( impl_.v_+impl_.vused_, other.impl_.v_[impl_.vused_] ); ++ impl_.vused_; } } Stack & operator=( const Stack & other ) { Stack temp( other ); impl_.Swap( other.impl_ ); return *this; } size_t Count() const { return impl_.vused_; } void Push( const T& t ) { if ( impl_.vused_ == impl_.vsize_ ) { Stack temp( impl_.vsize_*2+1 ); while ( temp.Count() < impl_.vused_ ) { temp.Push( impl_.v_[temp.Count()] ); } temp.Push( t ); impl_.Swap( temp.impl_ ); } else { construct( impl_.v_+impl_.vused_, t ); ++ impl_.vused_; } } T &Top() { if ( 0 == impl_.vused_ ) { throw "empty stack"; } returm impl_.v_[ impl_.vused_-1 ]; } void Pop() { if ( 0 == impl_.vused_ ) { throw "pop from empty stack"; } else { --impl_.vused_; destroy( impl_.v_+impl_.vused_ ); } }private: StackImpl< T > impl_;};
安全的异常
指导原则
- 永远不要在析构函数、重载的operator delete()或者operator delete[]()等函数中抛出异常:编写每个析构函数和内存释放函数时,都要假定在声明这些函数的时候使用了throw()异常规范
- 通过RAII(“资源获得也就意味着初始化”)这种惯用法来分离资源的所有权和资源的管理权
- 在每个函数中,将所有可能会抛出异常的代码放在一起,并进行安全处理,当确认这些代码所进行的工作都已经成功地完成时,才可以使用不会抛出异常的操作来修改程序的状态
参考资料:
《Exceptional C++中文版》
0 0
- 异常安全的复制构造函数和拷贝赋值运算符函数
- C++ 拷贝构造函数(即复制构造函数)和赋值运算符重载(有说赋值构造函数)区别
- 拷贝构造函数和赋值运算符
- 拷贝构造函数和赋值运算符
- 拷贝构造函数和赋值运算符
- 拷贝构造函数和赋值运算符
- 拷贝构造函数和赋值运算符
- 复制构造函数和赋值运算符
- 使用拷贝构造函数和赋值运算符的区别
- 类的赋值运算符重载和拷贝构造函数
- 对于拷贝构造函数和赋值运算符的理解
- 拷贝构造和赋值运算符函数的重载
- C++中拷贝(复制)构造函数和赋值运算符重载函数
- 关于复制构造函数(拷贝构造函数)和赋值操作符(拷贝赋值函数)应该知道的
- 赋值运算符和复制构造函数的区别
- 复制构造函数和赋值运算符根本的不同
- 类的构造函数、拷贝构造函数、赋值运算符和析构函数
- 再论拷贝构造函数和拷贝赋值运算符
- C# 特性(Attribute)
- c++拾遗-----处理数据
- OSTaskCreate返回值为66、OS_ERR_TASK_NO_MORE_TCB ucosII
- Ubuntu 14.04 安装kubernetes
- 30. Substring with Concatenation of All Words
- 异常安全的复制构造函数和拷贝赋值运算符函数
- COM 组件设计与应用(三)——数据类型
- 模板(一)
- Sicily 2005. Lovely Number
- HPU:1233畅通工程(kruskal)
- STM32Cube Printf uart重定向
- [基础]关于Java强制转换中无法强制转换String的思考
- js页面的取值,ajax
- 黑马程序员——字符串篇