I have written the following code to implement a stack.
#include<iostream>
using namespace std;
class stack
{
public:
int *top;
int size, capacity;
stack(int c):capacity(c){
size = 0;
top = new int[capacity];
capacity = capacity - 1;
}
~stack() // deleting the stack at the end of program
{
delete[] top;
top = NULL;
cout << "deleted the stack" << endl;
}
void push(int data)
{
// Is there is a space
if(is_stackFull())
{
cout << "stack is full" << endl;
return;
}
// populate the data
*top = data;
// updating the stack variables
top += 1;
size += 1;
return;
}
void pop()
{
// is stack empty
if(is_stackEmpty())
{
cout << "stack is empty" << endl;
return;
}
// update the stack variables
top -= 1;
size -= 1;
return;
}
bool is_stackFull()
{
return (size == capacity);
}
bool is_stackEmpty()
{
return (size == 0);
}
};
int main()
{
stack s(10);
s.push(15);
//s.pop(); // what happens to the output when i comment out this code?
return 0;
}
In the code, the destructor is not being invoked when I commented the s.pop() method.
Can any one explain the tech reason behind such behavior?
I tried understanding the ways to invoke the destructor in a C++ class.
In stack::push you are changing what top points to. Thus it can no longer be deleted. If you push once and pop once, top goes back to what it was, and the error does not occur.
Rather than modifying the top pointer, you can keep track of a top index and modify that. E.g.
#include <iostream>
#include <algorithm>
#include <optional>
template <class T>
class Stack {
public:
using size_type = std::size_t;
// Constructor
Stack(size_type cap)
: capacity(cap), top(0), data(new T[cap])
{ }
// Destructor
~Stack() {
delete[] data;
}
// Copy constructor
Stack(const Stack& s)
: capacity(s.capacity), top(s.top), data(new T[s.capacity])
{
std::copy(s.data, s.data+capacity, data);
}
// Copy assignment
Stack& operator=(const Stack& s) {
top = s.top;
capacity = s.capacity;
delete[] data;
data = new T[capacity];
std::copy(s.data, s.data+capacity, data);
return *this;
}
// Move constructor
Stack(Stack&& s)
: capacity(s.capacity), top(s.top), data(s.data)
{
s.capacity = 0;
s.top = 0;
s.data = nullptr;
}
size_type get_capacity() const { return capacity; }
size_type get_top() const { return top; }
bool is_empty() const { return top == 0; }
bool is_full() const { return top == capacity; }
bool push(T val) {
if (is_full()) return false;
data[top++] = val;
return true;
}
std::optional<T> pop() {
if (is_empty()) return std::nullopt;
return std::optional<T>{data[--top]};
}
private:
T *data;
size_type top;
size_type capacity;
};
int main() {
auto s = Stack<int>(10);
s.push(19);
s.push(67);
std::cout << s.pop().value() << " "
<< s.pop().value() << std::endl;
}
The destructor is being invoked, it's just not doing what you expect. When you call push but not pop, top ends up pointing one space to the right of where it started. So, the destructor ends up calling delete on a different address from the one returned by new, which is undefined behavior. That means your code can basically do whatever it wants, which in this case apparently means not printing your deleted the stack message. When I run it, I get
free(): invalid pointer
Aborted (core dumped)
To fix it, you could add a line top -= size to your destructor before you call delete so that it is pointing to the same place it started.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With