My assignment is to implement a stack (array-based) with a given capacity that, when attempting to add another element after the stack is full, will grow by a constant value (I used 100).
The problem here I believe lies in my push() function, which adds 100 element to the stack... probably syntactical but I'm not sure at all why my program won't execute.
template<class Type>
class ArrayStack{
enum {default_cap=100};
private:
Type* S; //array storing elements
int CAP; //capacity of stack
int TOP; //top element of stack
public:
ArrayStack(int defc = default_cap); //constructor with default parameter
~ArrayStack(){} //is "delete [] S;" supposed to go in here? not sure
bool isEmpty() const { return (TOP<0); }//is the stack empty?
int size() const { return (TOP+1); }
const Type& top(){ return S[TOP];} //has exception handling, not displayed
Type pop() {--TOP;} //removes top element
//here's the function that concerns me:
//--------------------------------------------
void push (const Type& e){
if(size() == CAP) {
Type* Snew = new Type[CAP+100];
for(int i = 0; i < CAP; i++){
Snew[i] = S[i];
}
delete [] S;
++CAP;
S = Snew;
}
S[++TOP] = e;
}
//--------------------------------------------
//other functions...
};
//constructor:
template<typename T> ArrayStack<T>::ArrayStack(int d)
: S(new T[d]), CAP(d), TOP(-1){}
It's a bit hard to comment since you've only provided partial code, and haven't demonstrated usage (e.g. with a main() function).
However, an obvious problem [which I notice Roger Rowland has identified in his comment too] with the push() function is that it increases allocated size by 100, but only increments CAP. So it will add 100 elements to the array, but only report ability to use the first one added.
The pop() function also discards the top element, and doesn't return it. If the caller ever tries to use the return value from pop() - and users of a stack type do normally expect to be able to use values they pop - the result will be undefined behaviour.
Your destructor definitely needs to use operator delete, unless you clean up dynamically allocated memory in some other way (and you've shown nothing like that). The whole point of operator new is that memory is NOT released until a corresponding operator delete. It will not be cleaned up magically if you forget to do it, and will present a memory leak for (at least) as long as your program runs.
If you want to do things a bit more safely, use a std::vector instead of a pointer (and avoid using operator new directly).
Related
I developped a blocking queue class as follow
class Blocking_queue
{
public:
Blocking_queue();
int put(void* elem, size_t elem_size);
int take(void* event);
unsigned int get_size();
private:
typedef struct element
{
void* elem;
size_t elem_size;
struct element* next;
}element_t;
std::mutex m_lock;
std::condition_variable m_condition;
unsigned int m_size;
element_t* m_head;
element_t* m_tail;
};
I want the class to be as generic as possible so I'm using a void pointer which is allocated when the element is added to the queue and freed when removed from it.
int Blocking_queue::take(void* event)
{
element_t* new_head = NULL;
int ret = 0;
// Queue empty
if(nullptr == m_head)
{
// Wait for an element to be added to the queue
std::unique_lock<std::mutex> unique_lock(m_lock);
m_condition.wait(unique_lock);
}
if(nullptr == realloc(event, m_head->elem_size))
{
ret = -1;
}
else
{
// Take element from queue
memcpy(event, m_head->elem, m_head->elem_size);
ret = m_head->elem_size;
new_head = m_head->next;
free(m_head->elem);
free(m_head);
m_head = new_head;
if(nullptr == m_head)
{
m_tail = nullptr;
}
m_size -= 1;
}
return ret;
}
If the queue is empty, take() function waits on m_condition until a new element is added.
A pointer event has to be given to copy element's content before freeing it.
To be sure that the given pointer has the right size to copy element's content I reallocate the pointer with its size.
The problem I have with this is that it doesn't allow to pass a function's locale variable because it's allocated on the stack.
So if I do something like this
void function()
{
unsigned int event = 0;
queue->take(&event);
}
I'll have a invalid old size error on realloc.
So if I pass a null pointer or a heap allocated variable it'll work but if I pass a stack variable address it won't.
Is there a way to allow stack variable address to be passed to take() function ?
Is there a way to allow stack variable address to be passed to take()
function ?
The short answer is no. malloc()/free()/realloc() can only work with heap-allocated memory; they will not work with stack-allocated memory.
As for how you might work around this problem, I think it will require some redesign. My first suggestion is to run as far away as possible from (void *) -- void-pointers are extremely unsafe and difficult to use correctly, because the compiler knows nothing about what they point to, and therefore cannot generate errors when the programmer does something incorrectly; this leads to lots of runtime problems. They are more of a C-language construct, still supported in C++ to provide C compatibility, but C++ has better and safer ways to do the same things.
In particular, if all of the data-elements of your queue are expected to be the same type, then the obvious thing to do would be to make your Blocking_queue class templated with that type as a template-argument; then the user can specify e.g. Blocking_queue<MyFavoriteDataType> and use whatever type he likes, and provide easy-to-use by-value semantics (similar to those provided by e.g. std::vector and friends)
If you want to allow mixing data-elements of different types, then the best thing to do would be the above again, but define a common base-class for the objects, and then you can instantiate a Blocking_queue<std::shared_ptr<TheCommonBaseClass> > object that will accept shared-pointers to any heap-allocated object of any subclass of that base class. (If you really need to pass shared-pointers to stack-allocated objects, you can do that by defining a custom allocator for the shared pointer, but note that doing so opens the door to object-lifetime-mismatch issues, since the stack objects may be destroyed before they are removed from the queue)
This question already has answers here:
How to resize array in C++?
(5 answers)
Closed 4 years ago.
I am sorry if this has already been covered before. I know how to do this is C and Java but not C++. Without using a pre-existing class which includes the use of Vector, how would you increase the size of an array given the code below?
The array expansion and assignment to the array takes place in push() noted with the all caps comment.
EDIT: As I have mentioned in comments below this is a question regarding manually reallocating arrays rather than using std::vector or "Dynamic Arrays."
Line.h
#include <iostream>
#include "Point.h"
using namespace std;
class Line {
public:
Line();
virtual ~Line();
// TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
void push(const Point& p);
private:
unsigned int index; // size of "points" array
Point* points;
};
Main.cpp
#include <iostream>
#include "Point.h"
#include "Line.h"
using namespace std;
int main() {
int x, y;
int size; // Some user defined size for the array
Line line;
Point a[size]; // Some points that are already filled
// Push the data in a[] to the variable "line"
for(int i = 0; i < size; i++){
// Increase array size of Point* points in variable line and add a[i] to the end of the array
line.push(points[i]);
}
return 0;
}
The simple answer is you should always use std::vector in this case. However it might be useful to explain just why that is. So lets consider how you would implement this without std::vector so you might see just why you would want to use std::vector:
// Naive approach
Line::push(const Point& p)
{
Point* new_points = new Points[index + 1];
std::copy(std::make_move_iterator(points), std::make_move_iterator(points+index), new_points);
new_points[index] = p;
delete[] points;
points = new_points;
index += 1;
}
This approach has many problems. We are forced to reallocate and move the entire array every time an entry is inserted. However a vector will pre-allocate a reserve and use space out of the reserve for each insert, only re-allocating space once the reserve limit is surpassed. This mean vector will far out perform your code in terms of performance as less time will be spent allocating and moving data unnecessarily. Next is the issue of exceptions, this implementation has no exception guarantees, where as the std::vector provides you with a strong exception guarantee: https://en.wikipedia.org/wiki/Exception_safety. Implementing a strong exception guarantee for your class is none trivial, however you would have automatically got this had you implemented this in terms of std::vector as such
Line::push(const Point& p)
{
points.push_back(p);
}
There are also other more subtle problems with your approach, your class does not define copy or assignment operators and so gets compiler generated shallow copy versions generated which means if someone copies your class then allocated members will get deleted twice. To resolve this you need to follow the rule of 3 paradigm pre C++11 and the rule of 5 for C++ 11 onwards: https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming). However had you used a vector none of this would be needed as you would benefit from the rule of zero and be able to rely on the compiler generated defaults: https://blog.rmf.io/cxx11/rule-of-zero
Essentially the only way is to use a dynamic array (one created using new[]) and to create an entirely new dynamic array and copy (or move) the objects from the old array to the new one.
Something like this:
class Line {
public:
Line(): index(0), points(nullptr) {} // initialize
virtual ~Line() { delete[] points; } // Clean up!
void push(const Point& p)
{
// create new array one element larger than before
auto new_points = new Point[index + 1];
// copy old elements to new array (if any)
for(unsigned int p = 0; p < index; ++p)
new_points[p] = points[p];
new_points[index] = p; // then add our new Point to the end
++index; // increase the recorded number of elements
delete[] points; // out with the old
points = new_points; // in with the new
}
private:
unsigned int index; // size of "points" array
Point* points;
};
But this approach is very inefficient. To do this well is quite complex. The main problems with doing things this way are:
Exception safety - avoiding a memory leak if an exception is thrown.
Allocation - avoiding having to reallocate (and re-copy) every single time.
Move semantics - taking advantage of some objects ability to be moved much more efficiently than they are copied.
A (slightly) better version:
class Line {
public:
Line(): index(0) {} // initialize
virtual ~Line() { } // No need to clean up because of `std::unique_ptr`
void push(const Point& p)
{
// create new array one element larger than before
auto new_points = std::unique_ptr<Point[]>(new Point[index + 1]);
// first add our new Point to the end (in case of an exception)
new_points[index] = p;
// then copy/move old elements to new array (if any)
for(unsigned int p = 0; p < index; ++p)
new_points[p] = std::move(points[p]); // try to move else copy
++index; // increase the recorded number of elements
std::swap(points, new_points); // swap the pointers
}
private:
unsigned int index; // size of "points" array
std::unique_ptr<Point[]> points; // Exception safer
};
That takes care of exception safety and (to some degree - but not entirely) move semantics. However it must be pointed out that exception safety is only going to be complete if the elements stored in the array (type Point) are themselves exception safe when being copied or moved.
But this does not deal with efficient allocation. A std::vector will over allocate so it doesn't have to do it with every new element. This code also misses a few other tricks that a std::vector would employ (like allocating uninitialized memory and constructing/destructing the elements manually as and when they are needed/discarded).
You basically have no way but to allocate a new array, copy existing values inside and delete [] the old one. That's why vector is doing the reallocation by a multiplicative factor (say each reallocation doubles the size). This is one of the reasons you want to use the standard library structures instead of reimplementing.
Keep It Simple
In my opinion, in this case, it's better to use a Linked-List of CPoint in CLine:
struct CPoint
{
int x = 0, y = 0;
CPoint * m_next = nullptr;
};
class CLine
{
public:
CLine() {};
virtual ~CLine()
{
// Free Linked-List:
while (m_points != nullptr) {
m_current = m_points->m_next;
delete m_points;
m_points = m_current;
}
};
// TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
void push(const CPoint& p)
{
m_current = (((m_points == nullptr) ? (m_points) : (m_current->m_next)) = new CPoint);
m_current->m_x = p.m_x;
m_current->m_y = p.m_y;
m_index++;
};
private:
unsigned int m_index = 0; // size of "points" array
CPoint * m_points = nullptr, * m_current = nullptr;
};
.
Or, even better with smart pointers:
#include <memory>
struct CPoint
{
int m_x = 0, m_y = 0;
std::shared_ptr<CPoint> m_next;
};
class CLine
{
public:
CLine() {};
virtual ~CLine() {}
// TAKE IN NEW POINT, INCREASE THE ARRAY SIZE AND ADD NEW POINT TO THE END OF THE ARRAY
void push(const CPoint& p)
{
m_current = (((m_points == nullptr) ? (m_points) : (m_current->m_next)) = std::make_shared<CPoint>());
m_current->m_x = p.m_x;
m_current->m_y = p.m_y;
m_index++;
};
private:
unsigned int m_index = 0; // size of "points" array
std::shared_ptr<CPoint> m_points, m_current;
};
Hello I have a problem with pointer on struct in a stack.
I have a stack of struct:
stack<Somethink*> stack1;
And i want to push and pop array of "Somethink"
void Search(Somethink* array_Somethink, int s, int d,) {
stack1.push(&(array_Somethink[s])); //
while (stack1.size() != 0) {
int i = 0;
array_Somethink[i] = *(stack1.pop()); // this return a error
i++;
}
}
I hope someone can give me a tip, how to properly push and pop from this stack
Thank you :D
void Search(Somethink* array_Somethink, int s, int d,) {
stack1.push(&(array_Somethink[s])); //
while (!stack1.empty()) {
int i = 0;
array_Somethink[i] = *(stack1.top());
stack1.pop();
i++;
}
}
My modified code assumes, you have "owning" pointers to the elements on the stack somewhere else. If that is not the case, you would end with memory leaks here, as the pointers in the stack become dangling objects (leaks).
In order to avoid the potential for memory leaks, here, it might be a good idea if you used std::shared_ptr<Somethink> instead of raw pointers. Then, your stack would become a std::stack< std:shared_ptr<Somethink> >.
For details on std::stack operations empty(),pop(),top(), see std::stack in the usual place.
There, you will find explanations such as this:
std::stack::top
C++ Containers library std::stack
reference top();
const_reference top() const;
Returns reference to the top element in the stack. This is the most recently pushed element. This element will be removed on a call to pop(). Effectively calls c.back().
top will return a pointer to the struct and you are trying to assign it to an instance of the struct. Basically you are trying to assign a pointer to Somethink to a position in an array of Somethink's
My Problem is my delete function is not working.When i pushed element
onto the stack then display it and after that if i delete the whole stack so when i displayed it
it should output garbage values but this is not the case it is showing the same values again as if nothing deleted.
#include<iostream.h>
#include<conio.h>
template<class T>
class stack
{
int capacity;
public:
T *array;
int top;
stack(int);
void push(T);
T pop();
int stackempty();
int stackfull();
void display();
void deletestack();
};
template<class T>
stack<T>::stack(int max)
{
top=-1;
capacity=max;
array=new T[capacity];
}
template<class T>
void stack<T>::push(T data)
{
if(stackfull())
cout<<"stack overflow";
else
array[++top]=data;
}
template<class T>
T stack<T>::pop()
{
if(stackempty())
{
cout<<"stack underflow";
return -1;
}
else
return array[top--];
}
template<class T>
int stack<T>::stackempty()
{
if(top==-1)
return 1;
else
return 0;
}
template<class T>
int stack<T>::stackfull()
{
if(top==capacity-1)
return 1;
else
return 0;
}
template<class T>
void stack<T>::display()
{
int i;
if(top==-1)
cout<<"empty stack";
else
for(i=0;i<=top;i++)
cout<<"\n"<<array[i];
}
template<class T>
void stack<T>::deletestack()
{
delete []array;
}
int main()
{
stack<int>* S=new stack<int>(20);
clrscr();
S->push(5);
S->push(10);
S->push(15);
S->display();
S->deletestack();
S->display();
S->push(5);
S->display();
getch();
return 0;
}
That's a lot more code than necessary to illustrate this problem, but anyway... If you delete[] an array, it is Undefined Behaviour to access it again. By accessing it again, your program could do anything at all.
That includes displaying the values that were already in this memory. These are garbage. They just happen to be garbage that looks a lot like the values your already put here. The reason this happens is that it would be extra work to go through and fill this memory with, say, zeroes. You're not allowed to look at it any more, so who cares what is there.
Imagine you owned some houses in a colony. Now you don't own them. This doesn't mean that those houses are destroyed. It may happen that the houses are there as it is. It may happen there is no house there.
Similarly, the memory alloted for your array is still there and you have its address. It may happen that location has same data or not.
The line delete []array; in your method deletestack() will free the array of memory that the pointer called array is pointing at. (however free is not the same as write over it with something else)
After you call delete you return the memory to the operating system, accessing it after it was returned to the operating system is undefined behavior. Sometimes it will cause a segmentation fault and a crash and sometimes nothing bad will happen. C++ in this respect is very different from languages like Java that actively look for illegal memory access and immediately throw an exception. In C++ the programmer is expected to follow the rules and no one is checking after him.
In any case freeing the memory does not mean that it is cleared. The old values you placed in there are still there until something else will get this memory allocated and start writing to it.
There are many other issues in the code you posted that will cause weird behavior if it is used:
stack<int>* S=new stack<int>(20); allocated an object on the heap that is never deleted.
the class stack allocates an array during construction however the dtor does not release the array. A user who forgets to call deletestack will cause memory to leak
the class stack holds a reference to an allocated array however it does not have a copy-ctor or a operator= and it does not delete them.
I'm fairly new to C++ and new to pointers as well. I'm currently working on a stack and was trying to reallocate the memory for the stack as the size of the stack reaches the top however, I'm running into issues. I've already done a lot of research both on Google and stack overflow and have found some information helpful but since I'm so new to stacks and C++ I'm still having issues. I was hoping some bright and intelligent people could at least point me in the right direction.
now... Here's my code.
#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);
}
Here's a list of a few things I noticed:
STACKMAX is a constant. If you're expanding the stack, how will you keep track of how big it currently is?
The values member is a fixed-size array. You won't be able to change the size of it dynamically without changing how this is declared and allocated.
calloc() allocates a new chunk of memory with the number of bytes you specify. You'll need to somehow copy the existing stack into the new memory block, and free the previous one.
You're allocating only STACKMAX bytes in the call to calloc(). You'll probably want to scale this by sizeof T, in case T is not a char.
There will be a lot of details for you to fix up once you address these major points. Good luck.
The problem is that you don't want to reallocate the top of the stack. Rather, you want to allocate a new array of values which is large enough to hold the new values. Also, since you need to reallocate the array, values should be a pointer.
But how about we forget all this. If we're working in c++, let's use what c++ offers us to make our lives easier. After that's done, then try open things up, if you really feel the need.
One of the things I'm referring to is your use of calloc. Using calloc is a bad idea, particularly when using templates. The problem is that since calloc has no type information, it won't do something as basic as calling a constructor. Constructors are very important in OOP, since they guarantee that an object's invariance when it is created. Instead, use the new[] keyword, like
values = new T[STACKMAX];
This allocates an array of T of STACKMAX length. Of course, as Greg points out, you should reconsider the use of STACKMAX, and use a variable instead. Also, values shouldn't be a static array, but should instead have type T*.
Another thing I was referring to is the fact that you are really trying to implement an array which grows dynamically as needed. In c++, we call such a structure a vector. If you use a vector, your entire code reduces to
#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;
}
That's all. It's a lot less hairy if you can use an existing data structure to implement the new data structure.
Once you get really comfortable with how a vector works (and there's plenty of explanations / documentation on the web), then try implementing the functionality yourself. Bottom line is, implementing a data structure is a lot easier if you know exactly how it's supposed to behave.
I would declare your values like
T* vaules;
Then use new to create it not calloc. You will need to keep track of the top of the stack and size of it. As Greg says when you grow the stack make sure and copy data over and clean up the old one.