Exceptional C++

【Exceptional C++(6)】异常处理的安全性

2018-01-26  本文已影响14人  downdemo

问题

template <class T>
class Stack {
public:
    Stack();
    ~Stack();
    Stack(const Stack&);
    Stack operator=(const Stack&);
    unsigned Count(); // 返回T在栈中的数目
    void Push(const T&);
    T pop(); // 如果为空返回缺省构造出来的T
private:
    T* v_;
    unsigned vsize_; // v_区域大小
    unsigned vused_; // v_区域中实际使用的T数目
};

说明

解答

// 默认构造
template<class T>
Stack<T>::Stack()
  : v_(new T[10]),
    vsize_(10),
    vused_(0)
{
    // 若程序到达这里说明构造过程没问题
}
// 拷贝构造
template<class T>
Stack<T>::Stack(const Stack<T>& other)
  : v_(0),
    vsize_(other.vsize_),
    vused_(other.vused_)
{
    v_ = NewCopy(other.v_, other.vsize_, other.vsize_);
    // 若程序到达这里说明拷贝构造过程没问题
}
// 拷贝赋值
template<class 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_;
        // 这里不能抛出异常,因为T的析构函数不能抛出异常
        // ::operator delete[]被声明成throw()
        v_ = v_new;
        vsize_ = other.vsize_;
        vused_ = other.vused_;
    }
    return *this; // 很安全,没有拷贝问题
}
// 析构
template<class T>
Stack<T>::~Stack()
{
    delete[] v_; // 同上,这里也不能抛出异常
}
// 计数
template<class T>
unsigned Stack<T>::Count()
{
    return vused_; // 只是一个内建类型,不会有问题
}
// push
template<class T>
void Stack<T>::Push(const T& t)
{
    if (vused_ = vsize_) // 可以随需要而增长
    {
        unsigned vsize_new = (vsize + 1) * 2; // 增长因子
        T* v_new = NewCopy(v_, vsize_, vsize_new);
        // 若程序到达这里,说明内存分配和拷贝过程都没问题
        delete[] v_; // 同上,这里也不能抛出异常
        v_ = v_new;
        vsize_ = vsize_new;
    }
    v_[vused_] = t; // 如果这里抛出异常,增加操作不会执行
    ++vused_; // 状态也不会改变
}
// pop
template<class T>
T Stack<T>::Pop()
{
    T result;
    if (vused_ > 0)
    {
        result = v_[vused_-1]; // 如果这里抛出异常,减操作不会执行
        --vused_;
    }
    return result;
}
// pop强迫使用者编写非异常安全代码
// 这首先就产生一个副作用,从栈中pop一个元素
// 解决办法是把函数重构成void Stack<T>::Pop(T& result)
// 这样可以在栈状态改变前得知结果的拷贝是否成功
template<class T>
void Stack<T>::Pop(T& result)
{
    if (vused_ > 0)
    {
        result = v_[vused_ - 1];
        --vused_;
    }
}
// 辅助函数
// 当把T从缓冲区拷贝到更大的缓冲区时
// 辅助函数分配新缓冲区并拷贝元素
// 如果这里异常,辅助函数释放所有临时资源
// 并把异常传递出去,保证不发生内存泄漏
template<class T>
T* NewCopy(const T* src, unsigned srcsize, unsigned destsize)
{
    destsize = max(srcsize, destsize); // 基本的参数检查
    T* dest = new T[destsize];
    // 如果程序到达这里说明内存分配和构造函数没问题
    try
    {
        copy(src, src + srcsize, dest);
    }
    catch(...)
    {
        delete[] dest;
        throw;
    }
    // 如果程序到达这里说明拷贝操作也没问题
    return dest;
}
上一篇下一篇

猜你喜欢

热点阅读