如何使堆栈动态增长?

时间:2015-03-15 17:38:55

标签: c++ class memory stack heap

这是我的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;
}

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

3 个答案:

答案 0 :(得分:1)

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


实现你想要的一种非常天真的方式是:

  1. 在每次添加元素时,使用new[]分配 size + 1 的新数组。
  2. 将旧数组的所有元素和新元素复制到新数组。
  3. delete[]旧数组。
  4. stackPtr指向新数组。
  5. 除了这个解决方案的所有性能和异常安全隐患之外,如果您的元素类型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,但这样做有点题外话)。

    使用placement new还可以使std::vector的容量呈指数级增长。每次添加元素时,容器都不需要重新分配内存;它提前为更多元素分配原始内存(类似于Christophe在他的回答中所做的)。仅在超过当前容量时才会重新分配。

    使用new[]delete[],这样一个精心设计的机制是不可能的。


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

答案 1 :(得分:0)

我想建议使用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新版本来避免在内存不足的情况下抛出异常,因为您的签名预见到操作成功的返回代码。

答案 2 :(得分:0)

您始终可以使用链接列表进行此操作。如果要插入堆栈,则只需将节点放在链表的头部即可。要弹出,只需将其删除即可。现在您可以考虑如何实现Stack S=new Stack(5);之类的语句。

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