通用堆栈类的异常安全代码

时间:2014-04-02 18:26:47

标签: c++ exception generics c++11

我正在尝试编写一个异常安全的通用堆栈。这是我到目前为止所做的。

#include <iostream>
#include <memory>
#include <exception>

class stk_exception:public exception 
{
    virtual const char* what() const throw()
    {
        return "stack underflow";
    }
} stk_ex;

template <class T>
struct node
{
    T data;
    node<T> *next;
};

template <class T>
class stack_generic
{
public:
    stack_generic() : _head(nullptr) {
    }

    void push(T x) {
        node<T> *temp(new node<T>());
        temp->data = x;
        temp->next = _head;
        _head = temp;
    }            

    void pop() {
        if (_head == nullptr) {
            throw stk_ex;                
        } else {
            node<T> *temp = _head;
            _head = _head->next;
            delete temp;
            return;
        }
    }
    T top() {
        T x = T();
        if (_head == nullptr) {
            throw stk_ex;                
        } else {
           return _head->data;
        }
    }

private:
    node<T> *_head;
};

int main()
{
    stack_generic<int> s;
    s.push(1);
    s.push(2);
    std::cout << s.top();
    s.pop();
    std::cout << s.top();
    s.pop();
}

我本可以使用STL列表/向量来实现RAII,但我想使用原始指针。因此,当我使用unique_ptr将头指针包装在堆栈中时,它会抛出一个编译错误&#34;没有用于调用unique_ptr,default_delete的匹配函数。这里有什么问题?任何人都可以建议我该怎么做才能使这个类异常安全?谢谢!

编辑: 添加了下溢的异常处理。 定义了单独的top和pop方法

2 个答案:

答案 0 :(得分:5)

以下实现应该(几乎)是异常安全的:

void push(T x) {
    head = new node<T>{std::move(x), head};
}
T pop(void) {
    if (head) {
        T result{std::move(head->data)};
        auto old = head;
        head = head->next;
        delete old;
        return result;
    } else {
        cout << "underflow!";
        return T{};
    }
}

此代码的唯一问题是return result。通常,此操作可能会抛出异常,在这种情况下,调用者会看到异常,但堆栈仍然会更改。

您可以通过将功能分成两个功能来避免此问题。第一个函数返回top元素,第二个函数删除它。

答案 1 :(得分:2)

最佳做法是使用std::shared_ptr。您可以像这样实现类:

#include <iostream>
#include <memory>
#include <exception>


template <class T>
class node
{
public:
    node(T data, std::shared_ptr<node<T>> next)
        : _data(data), _next(next)
    {
    }

    T data() const
    {
        return _data;
    }

    std::shared_ptr<node<T>> next() const
    {
        return _next;
    }

private:
    T _data;
    std::shared_ptr<node<T>> _next;
};


template <class T>
class stack_generic
{
public:
    stack_generic()
        : _head(nullptr)
    {
    }

    void push(T x)
    {
        _head = std::make_shared<node<T>>(x, _head);
    }

    T pop()
    {
        if (_head == nullptr) {
            throw std::underflow_error("underflow");
        } else {
            std::shared_ptr<node<T>> temp = _head;
            _head = _head->next();
            return temp->data();
        }
    }

private:
    std::shared_ptr<node<T>> _head;
};


int main()
{
    stack_generic<int> s;
    s.push(1);
    s.push(2);
    std::cout << s.pop();
    std::cout << s.pop();
}

请注意以下事项:

  • 使用using namespace std;是不好的做法。
  • 对现代C ++程序使用nullptr而不是NULL
  • 使用下溢的异常来创建已定义的行为。
  • 在节点上使用访问器方法创建只读对象。
  • 为节点使用构造函数。
  • 使用例如 std::shared_ptr自动释放数据。