当我学习C ++时,我开始实现一些常见的数据结构作为一种实践形式。 第一个是Stack(这是第一个想到的)。 我已经做了一些编程并且它正在工作,但是现在我需要一些输入,否则我应该做什么。喜欢删除某些东西或其他专业提示。我应该做些什么不同以及为什么?
template <class T>
class Stack
{
private:
int* values;
int capacity;
int itemsOnStack;
public:
///////////////////
Stack()
{
Stack(32);
}
///////////////////
Stack(const int sz)
{
values = new T[sz];
capacity = sz;
itemsOnStack = 0;
}
~Stack()
{
values = 0;
// delete?
}
////////////////////
void Push(const T& item)
{
*(values + itemsOnStack) = item;
itemsOnStack++;
if(itemsOnStack > capacity)
{
capacity *= 2;
T* temp = new T[capacity];
temp = values;
values = new T[capacity];
values = temp;
}
}
///////////////////
T Pop()
{
if(itemsOnStack > 0)
{
int current = --itemsOnStack;
return *(values + current);
}
return NULL; // ? good?
}
///////////////////
T Peek()
{
if(itemsOnStack > 0)
{
int current = itemsOnStack - 1;
return *(values + current);
}
return NULL; // find something better here or shouldnt?
}
///////////////////
int Count()
{
return itemsOnStack;
}
///////////////////
int Capacity()
{
return capacity;
}
///////////////////
bool IsEmpty()
{
return itemsOnStack == 0;
}
};
答案 0 :(得分:7)
首次修复代码:
template <class T>
class Stack
{
private:
int* values;
int capacity;
int itemsOnStack;
public:
//Stack() :
//{
// Stack(32); // doesn't do what you expect. This would create an unnamed temporary stack object
//}
Stack(const int sz = 32) // C++ doesn't yet have delegating constructors. You can't call one ctor from another. But in this case, a simple default parameter can be used instead
: values(new T[sz]), capacity(sz), itemsOnStack() {} // use the initializer list for initializing members
~Stack()
{
delete[] values; // you allocated it, so you delete it as well
}
////////////////////
void Push(const T& item)
{
values[itemsOnStack] = item; // cleaner syntactically than your version
// *(values + itemsOnStack) = item;
++itemsOnStack; // prefer pre-increment by default.
if(itemsOnStack > capacity) // you need to check this before writing the element. Move this to the top of the function
{
int newCapacity = capacity * 2;
// what's this supposed to do? You're just copying pointers around, not the contents of the array
T* temp = new T[newCapacity ];
std::copy(values, values+capacity, temp); // copy the contents from the old array to the new one
delete[] values; // delete the old array
values = temp; // store a pointer to the new array
capacity = newCapacity;
}
}
///////////////////
T Pop()
{
T result = Peek(); // you've already got a peek function. Why not use that?
--itemsOnStack;
return result;
}
///////////////////
T Peek()
{
if(itemsOnStack > 0)
{
int current = itemsOnStack - 1;
return values[current]; // again, array syntax is clearer than pointer arithmetics in this case.
}
// return NULL; // Only pointers can be null. There is no universal "nil" value in C++. Throw an exception would be my suggestion
throw StackEmptyException();
}
///////////////////
int Count()
{
return itemsOnStack;
}
///////////////////
int Capacity()
{
return capacity;
}
///////////////////
bool IsEmpty()
{
return itemsOnStack == 0;
}
};
需要解决的问题:
复制构造函数和赋值运算符。目前,如果我尝试这样做,它将会非常可怕:
Stack<int> s;
Stack<int> t = s; // no copy constructor defined, so it'll just copy the pointer. Then both stacks will share the same internal array, and both will try to delete it when they're destroyed.
Stack<int> u;
u = s; // no assignment operator, so much like above, it'll blow up
Const正确性:
我不应该在Peek()
上致电Count()
或const Stack<T>
吗?
对象生命周期:
从堆栈中弹出元素不会调用元素的析构函数。推送元素不会调用元素的构造函数。简单地扩展数组会立即为所有新元素调用默认构造函数。当用户插入元素而不是befre时,应该调用构造函数,并在删除元素时立即调用析构函数。
而且,呃,正确的测试:
我没有以任何方式编译,运行,测试或调试它。所以我很可能错过了一些错误,并介绍了一些新错误。 ;)
答案 1 :(得分:4)
列举一些与约翰所说的一致:
1。)使用初始化列表
2.)在适用的情况下使用const成员函数
3.)使用与标准更紧密对齐的函数名称。 (empty()而不是IsEmpty())
4.)你有巨大的内存泄漏。在析构函数中销毁内存
答案 2 :(得分:3)
您有很多内存泄漏,尤其是在复制,分配或推送到Stack对象时。即使作为一个学习练习,我建议不要进行自己的内存管理(这是你需要在C ++中学习的东西,但是一步一步)。相反,使用std :: vector或std :: list而不是原始的C风格值数组。这意味着析构函数,复制构造函数,赋值运算符和Push()方法不会泄漏内存 - 默认情况下它们也会做正确的事情(我称之为最小惊喜原则 )。我认为C ++学习中最重要的部分之一就是知道如何使用STL。首先要理解指针,然后再考虑新的[]和删除[]。
我建议您阅读复制构造函数,赋值运算符和析构函数,以便了解如何实际复制和删除值。尝试写一堂课:
class Noisy {
public:
Noisy() { std::cout << "Constructor" << std::endl; }
~Noisy() { std::cout << "Destructor" << std::endl; }
Noisy& operator=(const Noisy& other) {std::cout << "Assign" << std::endl; }
Noisy(const Noisy& other) {std::cout << "Copy constructor" << std::endl; }
};
然后在Stack<Noisy>
上执行一些操作:
Stack<Noisy> stack(10);
stack.Push(Noisy());
Stack<Noisy> otherStack(stack);
Stack<Noisy> thirdStack;
thirdStack=stack;
看看你对你认为应该做的事情的期望是否与你在控制台窗口中看到的一致。
答案 3 :(得分:1)
您应该删除析构函数中的values
,而不是仅将其设置为0;
在push
方法中,您无法像这样调整数组大小。
有关调整数组大小的更多信息:http://www.daniweb.com/forums/thread13709.html#
答案 4 :(得分:0)
Push()
:你应该在写入之前检查数组的大小
答案 5 :(得分:0)
您需要采用四个成员变量的命名约定。我们使用
int *m_values;
int m_capactity;
等
你为什么要这样做
*(values + itemsOnStack) = item;
我认为你的意思是
values[itemsOnStack] = item;
他们完全一样,但第一个更自然