美文网首页程序员
异常安全的复制构造函数和拷贝赋值运算符函数

异常安全的复制构造函数和拷贝赋值运算符函数

作者: visionarywind | 来源:发表于2016-04-11 16:26 被阅读84次

异常安全性问题

  • 异常安全代码
    在出现异常的情况下仍然能够正确运行
  • 异常中立
    将所有的异常都转发给调用者
  • 永远不要在析构函数、重载运算符函数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++中文版》

相关文章

  • c++11 拷贝控制

    拷贝控制操作包括,拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,析构函数。拷贝和移动构造函数定义了用...

  • C++语法系列之13--拷贝构造函数总结

    1 拷贝构造函数/赋值运算符 一个问题:什么时候会调用拷贝构造函数和赋值运算符?总结如下:如下场景会调用复制构造函...

  • C++拷贝构造函数和拷贝赋值运算符问题

    为什么需要析构函数就几乎需要拷贝构造函数和拷贝赋值运算符?或者说拷贝构造函数和拷贝赋值运算符什么时候需要自己构造?...

  • 18/3

    1.特殊成员函数(6个):默认构造函数、复制构造函数、复制赋值运算符和析构函数、移动构造函数、移动赋值运算符 这些...

  • 2023-01-18 C++实现string类

    实现string类,用于学习 拷贝构造函数/拷贝赋值运算符/移动构造函数/移动赋值运算符

  • C++笔记:拷贝构造函数和赋值运算符重载

    拷贝构造函数和赋值运算符重载 拷贝构造函数 对于普通类型的对象来说,它们之间的复制是很简单的,例如: 而对于类对象...

  • (GeekBand)Second class

    一、Big Three:拷贝构造函数,拷贝赋值函数,析构函数 1.拷贝构造函数 文字定义:拷贝构造函数,又称复制构...

  • 构造函数涉及到的一些问题

    构造函数不同形式 默认的构造函数' 构造函数初始化列表; 拷贝构造函数; 复制运算符,如果类不允许复制,把拷贝构造...

  • C++语法系列之4

    1 复制构造函数的问题 如果没有显示的编写复制构造函数或者赋值运算符,编译器会自动生成默认的复制构造函数和赋值运算...

  • c++第三讲 类和对象

    目标: 类的默认成员函数 构造函数 析构函数 拷贝构造函数 赋值操作符重载 默认拷贝函数与赋值运算符重载 cons...

网友评论

    本文标题:异常安全的复制构造函数和拷贝赋值运算符函数

    本文链接:https://www.haomeiwen.com/subject/oyeilttx.html