I am trying to write a simple stack with dynamic memory allocation. Must be with new/delete. Sorry, if it's trivial but I would appreciate if someone could take a look. Any advice is appreciated.
It works, but I want to make peace with valgrind --leak-check=full
stack.cpp
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "stack.h"
#include <algorithm>
#define INITIAL_STACKSIZE 1
stack::stack()
{
top = 0;
size = INITIAL_STACKSIZE;
data = new int[size];
}
stack::~stack()
{
// delete [] ptr; makes it worse "free(): double free detected in tcache 2
// Aborted (core dumped)"
delete [] data;
}
void stack::push(int a)
{
if(top >= size)
{
int new_size = size * 2;
ptr = new int[new_size];
std:: copy(data, data + top, ptr);
data = ptr;
size = new_size;
}
data[top++]=a;
}
int stack::pop()
{
assert(top>0);
return data[--top];
}
stack.h
class stack
{
public:
void push(int a);
int pop();
void clear();
stack();
~stack();
private:
int top;
int size;
int *data;
int *ptr;
};
teststack.cpp
#include <stdio.h>
#include "stack.h"
int main()
{
stack s1;
stack s2;
s1.push(1);
s1.push(2);
s2.push(5);
s2.push(6);
printf("%d %d\n",s1.pop(),s2.pop());
printf("%d\n",s1.pop());
printf("%d\n",s2.pop());
return 0;
}
You need to delete[] data in your stack::push() before you assign ptr to data.
When you assign ptr to data as you do now before freeing the memory stored in data, you lose the pointer to the memory you allocated with new earlier (i.e., you leak memory).
Once you've copied everything out of data with std::copy(), it is safe to free the memory pointed to by data, since the memory has been copied.
So here is what your stack::push() should look like:
void stack::push(int a)
{
if(top >= size)
{
int new_size = size * 2;
ptr = new int[new_size]; // Or if ptr was a local variable,
// this line would be
// int ptr[] = new int[new_size];
std:: copy(data, data + top, ptr);
delete[] data;
data = ptr;
size = new_size;
}
data[top++]=a;
}
I also suggest you remove ptr from being a member variable of the stack class, and instead make it a local variable in the stack::push() method, since it isn't used anywhere else.
Related
I'm currently working with stack data structure in C++, and i'm trying to initialize the stack by using arrays.
Stack has a specific size and i want to print elements of the stack from top to bottom using for loop.
Here is my code:
#include <iostream>
using namespace std;
template<class T>
class Stack {
private:
int top, size;
T* stack = new T[size];
public:
Stack(int size) {
this->top = -1;
this->size = size;
}
void push(int data) {
if (top >= size - 1) {
return;
}
else {
++top;
stack[top] = data;
}
}
void pop() {
if (top < 0) {
return;
}
else {
--top;
}
}
void print() {
for (int i = top; i >= 0; i--) {
cout << stack[i] << endl;
}
}
};
int main() {
Stack<int> stack(20);
stack.push(12);
stack.push(3);
stack.push(4);
stack.push(7);
stack.print();
return 0;
}
After compiling i get all of my elements printed successfully, but unfortunately at the end the program throws an exception ntdll.pdb not loaded.
Thanks in advance.
The order of construction is wrong. The member variables are initialized before the constructor body is entered, so size contains an arbitrary value at that time it's read to determine the size of the array. Furthermore you forgot to delete the array.
To fix the initialization order, initialize the array in the constructor and add a destructor. In the following code I simply use std::unique_ptr which automatically deletes the array contained in it.
Furthermore it's preferrable to not use the same identifier for parameters and members; a prefix usually is added to member variables to avoid this
#include <memory>
...
template<class T>
class Stack {
private:
size_t m_size;
int m_top;
std::unique_ptr<T[]> m_stack;
public:
Stack(size_t size)
: m_stack(new T[size]), m_size(size), m_top(-1)
{
}
...
This question already has answers here:
How do you 'realloc' in C++?
(4 answers)
What is The Rule of Three?
(8 answers)
Closed 2 years ago.
I have to create push function with dynamic allocation using new instead of realloc/malloc. I already build program working fine but with malloc and calloc functions and because im starting learning c++ i dont know how to change that to use new instead of malloc. I also should double memory avaible for stack while program run out of avaible memory. Here is stack.cpp code:
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "stack.h"
stack::stack()
{
this->top = 0;
this->data= (int *)malloc(0 * sizeof(int));
}
stack::~stack()
{
free(this->data);
}
void stack::clear()
{
this->top = 0;
}
void stack::push(int a)
{
i = i * 2;
assert(this->top < STACKSIZE);
int *ptr = (int *)realloc(data, (this->top + 1) * sizeof(int));
if (ptr == NULL)
{
return;
}
this->data = ptr;
this->data[this->top++] = a;
}
int stack::pop()
{
assert(this->top > 0);
return this->data[--this->top];
}
and there is header file:
#define STACKSIZE 20
class stack
{
public:
void push(int a);
int pop();
void clear();
stack();
~stack();
private:
int top;
int *data;
};
I'm attempting to implement an intvector in C++ and am getting a "Segmentation fault: 11" error. I understand this has something to do with memory management, and considering how new I am to C++ it could definitely be a pretty minor mistake. I debugged the code with valgrind and was given messages such as the following:
Use of uninitialized value of size 8, Invalid read of size 4,Conditional jump or move depends on uninitialized value(s).
My best guess is it has something to do with how I'm implementing the arrays. I originally had the arrays stored on the heap but changed it to the stack and still got the same error. I've already implemented an intvector in java, so I was attempting to use similar logic here, which perhaps is part of the issue.
#include <iostream>
#include "IntVector.h"
#include <cmath>
using namespace std;
int num_elements = 0;
int array_size = 0;
int expansion_factor;
void IntVector::expandArray(){
int tempArr[array_size*2];
for(int i =0;i<array_size;i++){
tempArr[i] = array[i];
}
array = tempArr;
array_size = array_size * 2;
}
void IntVector::add(int val){
int tempArr[array_size];
if(array_size == num_elements){
expandArray();
array[num_elements] = val;
}
else{
for(int i = 0;i<array_size;i++){
tempArr[i] = array[i];
}
tempArr[num_elements] = val;
array = tempArr;
}
num_elements++;
}
void IntVector::remove(int index){
}
int IntVector::get(int index) const{
return index;
}
void IntVector::removeLast(){
}
void IntVector::set(int index, int val){
}
std::string IntVector::toString()const {
return "";
}
IntVector::IntVector(int initial_size){
int* array = new int[initial_size];
}
IntVector:: ~IntVector(){
delete[] array;
}
int main(){
IntVector v(0);
v.add(5);
}
#ifndef INTVECTOR_H_
#define INTVECTOR_H_
using std::cout;
class IntVector {
private:
int* array;
int num_elements;
int array_size;
int expansion_factor;
void expandArray();
public:
void add(int val);
void remove(int index);
int get(int index) const;
void removeLast();
void set(int index, int val);
std::string toString() const;
IntVector(int initial_size);
~IntVector();
};
#endif
As mention in the comments, there are definitely some holes in your understanding of C++. Really when dealing with header files you should have a main.cpp, someotherfile.h, someotherfile.cpp. That just best practices to avoid redefinition errors.
There was quite a bit wrong with the way you accessed the private variable. If a class has a private( or even public) variable you don't have to redeclare it each time you want to change its value.
There were one or two major flaws with the way you expanded the vector. If the vector size is initialized to 0 then 0*2 is still 0 so you never actually increased the size. Secondly, when you set the original array = to the new array the new array was just a local array. This means that the memory wasn't actually allocated permanently, once the function ended the temparr was destroyed.
I know this was probably a lot but if you have any question feel free to ask.
main.cpp
#include "IntVector.h"
int main()
{
IntVector v;
IntVector x(10);
v.push(5);
v.push(5);
v.push(5);
v.push(5);
v.push(5);
v.print();
cout << endl;
x.push(5);
x.push(5);
x.push(5);
x.push(5);
x.push(5);
x.print();
return 0;
}
IntVector.h
#include <string>
#include <iostream>
using namespace std;
class IntVector {
private:
int *array;
int num_elements;
int array_size;
//int expansion_factor =; you would only need this if you plan on more than double the vector size
void expandArray(); //normally c++ array double in size each time they expand
public:
//Constructors
IntVector(); //this is a contructor for if nothing is called
IntVector(int initial_size);
//setters
void push(int val); //add
void pop(); //removelast
void remove(int index); //remove
void at(int index, int val); //set
//Getters
int at(int index);
//std::string toString(); I'm changing this to print
void print(); //will print the contents to the terminal
//Deconstructor
~IntVector();
};
IntVector.cpp
#include "IntVector.h"
//constructors
IntVector::IntVector() //no arguments given
{
array = new int[0];
num_elements = 0;
array_size = 0;
}
IntVector::IntVector(int initial_size)
{
array = new int[initial_size];
num_elements = 0;
array_size = initial_size;
}
void IntVector::expandArray()
{
int *tempArr;
if(array_size == 0){
array_size = 1;
tempArr = new int[1];
} else {
//make sure to allocate new memory
//you were creating a local array which was destroy after the function was completed
//using new will allow the array to exist outside the function
tempArr = new int[array_size * 2];
}
for (int i = 0; i < array_size; i++)
{
tempArr[i] = array[i];
}
//make sure to delete the old array otherwise there is a memory leak.
//c++ doesn't have a garbage collector
delete[] array;
array = tempArr;
array_size = array_size * 2;
}
void IntVector::push(int val)
{
num_elements++;
//checking if vector needs to increase
if (array_size <= num_elements)
{
expandArray();
array[num_elements-1] = val;
}
else
{
array[num_elements-1] = val;
}
}
void IntVector::remove(int index)
{
//not sure how to implment this becuase each element has to be a number.
}
int IntVector::at(int index)
{
return array[index];
}
void IntVector::pop()
{
num_elements = num_elements-1; //not really removing it from the "vector" but it won't print out again
}
void IntVector::at(int index, int val)
{
array[index] = val;
}
void IntVector::print()
{
for (int i = 0 ; i < num_elements; i++)
{
cout << array[i] << " ";
}
cout << endl;
}
IntVector::~IntVector()
{
delete[] array;
}
output
5 5 5 5 5
5 5 5 5 5
Hopefully, the comments help. I changed the name of the functions to better match the actual vecter class the already exists in C++. I think it's good to pick apart already defined functions like this because you get a better understanding of how they actually work and not just how to use them.
If you got any questions just leave a comment
I'm trying to initialize an empty stack of size 3, but my program is not letting me put NULL into even 1 of the elements. I'm not sure what the problem is. The program just stops working when it attempts to initialize one of elements.
Stack300::Stack300 ()
{
for (int i = 0; i < 3; i++)
{
stackArray[i] = '\0';
//stackArray[i] = i;
}
top = 0;
return;
}
My .h file.
#ifndef CONGERA2_H
#define CONGERA2_H
typedef float Element300;
class Stack300
{
public:
Stack300 ();
Stack300 (const int);
Stack300 (Stack300 &old);
~Stack300();
void push300(const Element300);
Element300 pop300();
void viewTB300();
void viewBT300();
private:
const int MAX_STACK = 80;
Element300 * stackArray;
int top;
};
#endif
And my main file.
#include <iostream>
#include "congera2.h"
using namespace std;
int main()
{
Element300 temp1 = 1.1;
Element300 temp2 = 2.2;
Element300 temp3 = 3.3;
Stack300 myStack;
Stack300 myStack2 (myStack);
/* myStack.push300(temp1);
myStack.push300(temp2);
myStack.push300(temp3);*/
cout << "hello";
return 0;
}
In your constructor, you are never allocating any memory to the 'stackArray' member. The following line would accomplish initializing a dynamic array of 3 floating point integers.
stackArray = new float[3];
You will then want to make sure this memory is deallocated in the destructor as well.
Edited to add some useful resources; these pages do a good job explaining the concept behind pointers and dynamic memory allocation:
Pointers,
Dynamic Memory
I need to create a class called DynamicArray based on the given main.cpp file. However I cannot make it work. The program crashes. I believe it should be the constructor problem. Please see the following codes with comments:
main.ccp
#include <iostream>
#include <dynamicarray.h>
using namespace std;
int main()
{
DynamicArray* arr=new DynamicArray(ARRAY_SIZE);
for (int i=0; i<25; i++) {
arr->add("test");
}
for (int j=0; j<10; j++){
arr->remove();
}
delete [] arr;
arr=NULL;
return 0;
}
dynamicarray.h
#ifndef DYNAMICARRAY_H
#define DYNAMICARRAY_H
#define ARRAY_SIZE 5
#include <iostream>
using namespace std;
class DynamicArray
{
DynamicArray* darr;
int del;
int occSize;
int totSize;
public:
DynamicArray();
~DynamicArray();
DynamicArray(int);
void add(string);
void remove();
int totalSize();
int occupiedSize();
DynamicArray operator= (string);
DynamicArray operator= (int);
};
#endif // DYNAMICARRAY_H
dynamicarray.cpp
#include "dynamicarray.h"
DynamicArray::DynamicArray()
{
darr=new DynamicArray[ARRAY_SIZE];
del=0;
occSize=0;
totSize=ARRAY_SIZE;
}
DynamicArray::DynamicArray(int n){
darr=new DynamicArray[n];
del=0;
occSize=0;
totSize=ARRAY_SIZE;
}
DynamicArray::~DynamicArray()
{
delete [] darr;
darr=NULL;
}
//adding s to the next available slot in the array. When the array is full,
//add() will grow the array by another ARRAY_SIZE
void DynamicArray::add(string s){
if (occSize<totSize){
darr[occSize]=s;
occSize++;
}else{
totSize=totSize+ARRAY_SIZE;
DynamicArray* temp=new DynamicArray[totSize];
for (int i=0; i<occSize; i++){
temp[i]=darr[i];
}
darr=temp;
delete [] temp;
temp=NULL;
darr[occSize]=s;
occSize++;
}
}
//remove the element where the index is poining to in the array.
//when there are more than ARRAY_SIZE empty slots left, the delete()
//will shrink the array by another ARRAY_SIZE
void DynamicArray::remove(){
darr[del]=NULL;
del++;
occSize--;
if (del>5){
DynamicArray* temp=new DynamicArray[totSize-ARRAY_SIZE];
int j=del;
for (int i=0; i<occSize; i++){
temp[i]=darr[del];
j++;
}
darr=temp;
del=0;
delete [] temp;
temp=NULL;
}
}
//returning the current max capacity size of the array
int DynamicArray::totalSize(){
return totSize;
}
//returning the number of occupied slots in the array
int DynamicArray::occupiedSize(){
return occSize;
}
DynamicArray DynamicArray::operator= (string s){
*this=s;
return *this;
}
DynamicArray DynamicArray::operator= (int n){
*this=n;
return *this;
}
Of course it crashes, as in the constructor of DynamicArray you create new instances of DynamicArray which calls the constructor which in turn creates new instances of DynamicArray... This infinite recursion will lead to a stack overflow which crashes the program.
If the DynamicArray should by a dynamic array (or vector) of strings, then it should contain an "array" of strings, either std::string objects or of pointers. Like
class DynamicArray
{
private:
char** darr; // The actual array of data
...
};
DynamicArray::DynamicArray(int n)
{
darr = new char*[n];
...
}