如何使堆栈动态增长

How to make a stack grow dynamically?

本文关键字:动态 堆栈 何使      更新时间:2023-10-16

这是我的Stack类的小程序:

 #include <iostream>
using namespace std;
#include <iomanip>
template <typename T>
class Stack
{
private:
    T *stackPtr; 
    int size; 
    T top; 
public:
    Stack(int = 10);
    ~Stack(); 
    bool push(const T  );
    bool pop(); 
    void printStack();
};
int main()
{
    Stack <int> myStack(5);

    cout << "Push 5 elements to stack: ";
    int ct = 0;
    while (ct++ != 5)
    {
        int temp;
        cin >> temp;
        myStack.push(temp);
    }
    myStack.printStack(); 
    cout << "nErasing two elements:n";
    myStack.pop(); 
    myStack.pop(); 
    myStack.printStack(); 
    return 0;
}

template <typename T>
Stack<T>::Stack(int s)
{
    size = s > 0 ? s: 10;   
    stackPtr = new T[size]; 
    top = -1; 
}

template <typename T>
Stack<T>::~Stack()
{
    delete [] stackPtr; 
}

template <typename T>
bool Stack<T>::push(const T value)
{
    if (top == size - 1)
        return false; 
    top++;
    stackPtr[top] = value; 
    return true; 
}

template <typename T>
bool Stack<T>::pop()
{
    if (top == - 1)
        return false; 
    stackPtr[top] = 0; 
    top--;
    return true; 
}

template <typename T>
void Stack<T>::printStack()
{
    for (int ix = size -1; ix >= 0; ix--)
        cout << "|" << setw(4) << stackPtr[ix] << endl;
}

因此,为了使用这样的堆栈,如果我需要 10 个元素,我应该在使用前在构造函数中声明它的大小,就像 Stack<int> newstack(10) 一样。那么,如果我不知道堆栈的最终大小该怎么办?如何通过推动元素来使其动态增长?我一直在寻找解决方案,但我所有的想法仍然都是计算元素,然后声明一个堆栈以适应元素编号。

首先,你知道C++内置std::stack,不是吗?对于其余的答案,我假设你有理由不使用它(也许这是一个学习练习)。


实现你想要的一个非常天真的方法是:

  1. 每次添加元素时,分配一个大小为 + 1 的新数组,并带有 new[] .
  2. 将旧数组和新元素的所有元素复制到新数组中。
  3. delete[]旧阵列。
  4. stackPtr指向新阵列。

撇开此解决方案的所有性能和异常安全陷阱不谈,如果您的元素类型T没有默认构造函数,它怎么可能工作?它甚至不会编译。实际上,您的类会失败,因为它是以下T

struct CannotUseInThisStack
{
    CannotUseInThisStack(int) {} // no default constructor
};
Stack<CannotUseInThisStack> s; // error

真正的解决方案是:不要使用new[]delete[]。根据std::vector(或std::deque 来实现您的堆栈,这正是std::stack默认所做的! std::vector支持开箱即用的动态增长,以更好的方式,无需在每次添加元素时连续重新分配,也不需要能够默认构造T

当然,

这理所当然地导致了一个问题,即std::vector如何才能做到这一切。

答案是std::vector,或者更确切地说,它的标准分配器std::allocator不是new[]delete[]方面实现的,而是在放置新方面实现的。内存分配和元素构造是分开的。请参阅std::allocator:allocate 。这解决了缺少默认构造函数的问题。首先分配原始内存,然后使用复制构造函数在该原始内存位置构造新元素(在 C++11 中,您也有完美的转发来就地构造T,但这有点偏离主题)。

使用放置新也允许std::vector的容量呈指数级增长。容器不需要在每次添加元素时重新分配内存;它预先为更多元素分配原始记忆(类似于克里斯托夫在他的答案中所做的)。仅当超过当前容量时,才会进行重新分配。

有了new[]delete[],这样复杂的机制是不可能的。


通常,如果您想了解严肃的容器设计,请查看编译器如何实现所有C++标准容器

您始终可以使用链接列表进行此操作。如果你想插入到堆栈中,那么你只需在链表的头部放置一个节点。要弹出,您只需将其删除即可。现在你可能会想如何实现这种语句,比如 堆栈S=new Stack(5); .

您只需保留一个计数器来保持此值。您可以对此变量进行任何检查(如堆栈溢出等)。您只需让它像 STL 支持C++一样动态,并且很容易使用它。

我想建议使用std::stack()不会是您所期望的答案;-)

您只需

在达到限制时分配更多空间:

template <typename T>
bool Stack<T>::push(const T value)
{
    if (top == size - 1) {
        //return false; 
        size_t s = size + 10;       // for example increase by 10
        T *ps = new (nothrow) T[s]; // allocate new region
        if (ps==nullptr) 
            return false;           // out of memory
        for (size_t i=0; i<size; i++) 
            ps[i] = stackPtr[i];  
        delete[] stackPtr;  
        stackPtr = ps; 
        size = s;                  // stack is now bigger,  
    }                              // so continue as if there was no problem
    top++;
    stackPtr[top] = value; 
    return true; 
}

问题是,增加多少堆栈。 你可以像这里一样使用常量方法,但最好考虑一个成员变量并在构造函数中初始化它。

编辑:我使用了带有nothrow的新内容,以避免在内存不足的情况下引发异常,因为您的签名预见了操作成功的返回代码。