假设我有一个MyStack类,它公开了:

class MyStack {
public:

template <typename T>
T* Push() {
    Reserve(sizeof(T)); // Make sure that the buffer can hold an additional sizeof(T) bytes , realloc if needed
    auto prev= _top;
    _top += sizeof(T);
    new (prev) T();
    return reinterpret_cast<T*>(prev);
}

template <typename T>
T* Pop() {
    _top -= sizeof(T);
    return return reinterpret_cast<T*>(_top);
}

bool Empty() const {
    return _bottom == _top;
}

private:
    char* _bottom;
    char* _top;
};

// Assumes all stack elements have the same type
template <typename T>
void ClearStack(MyStack& stack) {
    while (!stack.Empty()) {
        stack.template Pop<T>()->~T();
    }
}


这里有一个隐藏的错误。在T中构造MyStack::Push()可能会抛出异常,这会使堆栈缓冲区处于未定义状态(分配的空间将包含垃圾)。稍后,当调用ClearStack时,它将尝试将垃圾重新解释为T并调用其析构函数,这可能会导致访问冲突。

是否可以通过仅修改MyStack::Push()来修复此错误? (限制是因为这是一个外部代码,我们希望进行最小的更改,因此更新库相对容易)

我考虑过将MyStack::Push更改为:

T* Push() {
    auto prev = _top;
    T t();
    Reserve(sizeof(T));
    _top += sizeof(T);
    reinterpret_cast<T*>(prev) = std::move(t);
    return prev;
}


但是它看起来很糟糕,我什至不确定它不会调用任何UB(并且会强制T具有move构造函数)

这里有更好的解决方案来防止抛出构造函数吗? (最好是在MyStack::Push()内部进行小的更改)

最佳答案

这段代码怎么样:

template <typename T>
T* Push() {
    Reserve(sizeof(T));
    auto prev= _top;
    _top += sizeof(T);
    try {
        new (prev) T();
        return reinterpret_cast<T*>(prev);
    }
    catch (...) {
        Unreserve(sizeof(T)); //release the memory, optional?
        _top = prev;
        throw;
    }
}

关于c++ - 如何以异常安全的方式使用新的展示位置?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49966422/

10-12 04:10