动态堆栈内存重新分配

问题描述:

我对C++相当陌生,对指针也很陌生。我目前正在研究一个堆栈,并试图重新分配堆栈的内存,因为堆栈的大小达到顶端,但是我遇到了问题。我已经在Google和堆栈溢出方面做了很多研究,并且发现了一些有用的信息,但是因为我对堆栈和C++都很陌生,所以我仍然遇到问题。我希望一些明亮而聪明的人至少能指引我朝着正确的方向前进。动态堆栈内存重新分配

现在...这是我的代码。

#include <iostream> 
#define STACKMAX 20 
using namespace std; 

template <class T> class StackTemplated { 
private: 
    int top; 
    T values[STACKMAX]; 
public: 
    StackTemplated(); 
    void push(T i); 
    T  pop(void); 
    bool empty(void); 
}; 


template <class T> StackTemplated<T>::StackTemplated() { 
    top = -1; 
} 

template <class T>void StackTemplated<T>::push(T i) { 
if (top == STACKMAX - 1) { 

    // reallocate top of stack. (this is the area I'm having issues) 
    char * string1; 
    string1 = (char *)calloc(STACKMAX, sizeof(char)); 

     if (top == STACKMAX - 1) { 
      cout << "The stack didn't re-allocate."; 
      exit(1); 
     } 

} else { 
    top++; 
    values[top] = i; 
} 
} 

template <class T> T StackTemplated<T>::pop(void) { 
if (top < 0) { 
    printf("%", "Stack underflow!"); 
    exit(1); 
} else { 
    return values[top--]; 
} 
} 

template <class T> bool StackTemplated<T>::empty() { 
return (top == -1); 
} 
+1

请注意,“C/C++”不是一种语言。 C和C++是独立的语言;在这种情况下,您正在使用C++。 – Cameron

+0

我已经删除了问题和标签中对C的引用。 –

+0

让'pop()'返回一个对象以及弹出一个对象是一个相当不好的设计。这使得以异常安全的方式使用堆栈非常困难(这使得强壮的异常安全保证特别难以实现),因为从堆栈中弹出该值(其修改堆栈)的操作之后是操作将返回值复制到调用者的代码中(可能会抛出)。 (有关此事的更多讨论,请参见[这里](http://www.gotw.ca/gotw/008.htm)。) – Mankarse

问题是你不想重新分配堆栈的顶部。相反,你想分配一个足够大的新数组来保存新值。另外,由于您需要重新分配阵列,values应该是一个指针。

但是我们如何忘记这一切。如果我们使用C++,让我们使用C++提供给我们的东西,让我们的生活更轻松。在完成之后,如果你真的觉得需要,可以试着打开它。

我指的是你使用calloc。使用calloc是一个不好的想法,特别是在使用模板时。问题是因为calloc没有类型信息,所以它不会像调用构造函数那样做一些基本的事情。构造函数是在OOP中非常重要的,因为它们保证了对象在创建时的不变性。而是,使用new[]关键字,像

values = new T[STACKMAX]; 

该分配的STACKMAX长度的T阵列。当然,正如Greg指出的那样,您应该重新考虑使用STACKMAX,并改用变量。另外,values不应该是一个静态数组,而应该改为T*

我指的另一件事是,你真的想要实现一个根据需要动态增长的数组。在C++中,我们称这种结构为vector。如果您使用矢量,您的整个代码缩小到

#include<iostream> 
#include<vector> 

using namespace std; 

template<class T> class StackTemplated { 
private: 
    std::vector<T> vec; 

public: 
    StackTemplated() { } // the constructor is trivial; in fact, you can leave it out if you want 
    void push(T i); 
    T  pop(void); 
    bool empty(void); 
}; 

template<class T> 
void StackTemplated<T>::push(T i) { 
    vec.push_back(i); 
} 

template<class T> 
T StackTemplate<T>::pop(void) { 
    T top = vec.back(); 
    vec.pop_back(); 
    return top; 
} 

template<class T> 
bool StackTemplate<T>::isEmpty(void) { 
    return vec.size() == 0; 
} 

就是这样。如果你可以使用现有的数据结构来实现新的数据结构,那么它就少了很多。

一旦你对vector的工作方式(以及网络上的大量解释/文档)非常满意,然后尝试自己实现功能。底线是,如果你确切地知道它应该如何表现,实现一个数据结构要容易得多。

这里有几件事情,我注意到一个列表:

  • STACKMAX是一个常数。如果你正在扩展堆栈,你将如何跟踪它目前有多大?
  • values成员是一个固定大小的数组。您将无法动态更改其大小,而无需更改其声明和分配方式。
  • calloc()以您指定的字节数分配新的内存块。你需要以某种方式将现有的堆栈复制到新的内存块中,并释放前一个堆栈。
  • 您在拨打calloc()时只分配STACKMAX字节。如果T不是char,您可能希望按比例缩小sizeof T

一旦你解决了这些重要问题,会有很多细节供你修复。祝你好运。

我将宣布自己的价值观像

T* vaules; 

然后使用新创建它不释放calloc。您需要跟踪堆栈的顶部和大小。正如格雷格在堆栈中说的那样,确保将数据复制并清理旧数据。