Delete dynamically allocated array c++ - c++

bool StudentList::remove(const char * studentName)
{
for (int i = 0; i < MAX_SIZE; i++)
{
if (this->students[i]->isEqualTo(studentName)) // Finds a name to remove
{
cout << "Remove: "; // Displays name wished to be removed
students[i]->print();
// delete[] students[i]; - Crashes
// students[i] = NULL; - Replaces removed name with null, stops working.
// students[i]->~Student(); - Call deconstructor, Crashes.
return true;
}
}
return false;
}
I just want to remove a single element out of the array, but keeps crashing when i delete that element.
students[i] is a pointer array, and i need to remove selected elements

First quetion, if you really need to delete "Student" object. If yes, you can add some bad code like:
students[i] = nullptr;
If your students are stored not only in this array, you can make that storage responsible for their deleting. But both ways aren't very good because of using null pointers later. Learn how to use collections, for example vector. You will be able just remove the pointer from an array.

It seems that you want to delete each instance of students, if you could find the studentname.
students seems a two dimensional structure pointer to a pointer. ie; **students. But, you are deleting it in wrong way.As you first need to delete the instance of students[i], then delete the instance of students.
Also, since you are calling the destructor students[i]->~Student(); after deleting instance, it may crash again, as you have assigned student[i] = NULL. then it will be, NULL->~Student() -it will also lead crash.
You need to delete it in following way :
for (int i = 0; i < MAX_SIZE; i++)
{
if (this->students[i]->isEqualTo(studentName)) // Finds a name to remove
{
students[i]->~Student();
delete students[i];
students[i] = NULL;
}
}
delete[] students;
students = NULL;

Related

Vector of a class resetting member variables after for loop

I have an assignment where we need to use this basic structure of vectors and classes to learn about parent and child classes and polymorphism. Here is the code of the function I'm supposed to write:
void assignStudents(vector<Student*>& v) {
for (int i = 0; i < 5; i++)
{
cout << "Enter a study level: ";
string input;
cin >> input;
if (input == "graduate")
{
Graduate inputClass;
Student* inputParentClassPtr = &inputClass;
v.push_back(inputParentClassPtr);
v[i]->addToVector(input);
inputParentClassPtr = nullptr;
}
else if (input == "undergraduate")
{
Undergraduate inputClass;
Student* inputParentClassPtr = &inputClass;
inputParentClassPtr->addToVector(input);
v.push_back(inputParentClassPtr);
}
else
{
cout << "Please enter a valid response, either graduate or undergraduate" << endl;
i--;
}
}
for (size_t i = 0; i < v.size(); i++)
{
vector<string> studyLevels = v[i]->getStudyLevels();
size_t size = studyLevels.size();
for (int j = 0; j < size; j++)
{
cout << studyLevels[j];
}
}
}
I debug the program and every time the first for loop moves on to the next iteration, every member variable inside each object in my vector goes blank, but then when I add a new object into the vector, then call the addToVector() function they come back.
I added the bottom for loop to check if any editing is happening, and once I get to that bottom for loop, every member variable is empty again.
I have the Student class vector where I am adding Undergraduate and Graduate classes to. Every Student class has a protected vector inside called levels. I need to add the class to vector that holds all my objects, then edit the member variable vector to include the string representing the type of class it is.
Why do the member variables (levels) go blank every time it finishes an iteration of the for loop?
I'll just focus on one part, as the same issue appears twice in your code.
{
Graduate inputClass; // create local student "on the stack"
Student* inputParentClassPtr = &inputClass;
v.push_back(inputParentClassPtr); // store address of student
v[i]->addToVector(input);
inputParentClassPtr = nullptr; // has no real effect
} // inputClass goes out of scope and is destroyed here
When the block ends, the local "stack" variables from that block are destroyed. That means the Graduate object is no longer valid, and the pointer you stored in v is now pointing at something unusable.
To fix that, you need to create the objects in dynamic memory.
You should change your vector to store std::unique_ptr<Student>, and create the objects using std::make_unique(), like this:
auto inputParentClassPtr = std::make_unique<Graduate>();
v.push_back(std::move(inputParentClassPtr));
But, if you can't do that, you will need to use new instead, like this:
Student* inputParentClassPtr = new Graduate();
v.push_back(inputParentClassPtr);
Either way, even though inputParentClassPtr is still destroyed at the end of the block, it is only a pointer and the Graduate object it pointed to is still alive.
If you use new, you'll then need to delete all the objects in the vector when you are done using them, or you'll have a memory leak. Using std::unique_ptr will handle that for you.

How to store multiple queue in array?

I have a queue class where I implement the queue structure.
#include "queue.h"
Queue::Queue()
{
}
Queue::Queue(int size){
front = rear = -1;
this->size = size;
Q = new int[size];
}
void Queue::enqueue(int x){
if (rear == size -1 ){
cout << " its full" << endl;
}else{
rear ++;
Q[rear] = x;
}
}
int Queue::dequeue(){
int x= -1;
if (rear == front){
cout << " its empty"<<endl;
}else{
front ++;
x = Q[front];
}
return x;
}
void Queue::Display(){
for(int i= front+1; i<=rear; i++){
cout << Q[i] << " ";
}
cout << endl;
}
bool Queue::isEmpty(){
return (size==0);
}
int Queue::peek()
{
if (isEmpty())
{
cout << "UnderFlow\nProgram Terminated\n";
exit(EXIT_FAILURE);
}
return Q[front];
}
In main.cpp, I create multiple queues. I am trying to implement a scheduling algorithm where I need to process each queue in order. The problem starts when I try to go through each queue. I would like to use only one for loop to access the element of each queue rather than for loop for each of them.
Example:
queue[1..N] where N is the number of queues. In for loop, I want to check if queue[i].empty().
I found a solution to the problem. In the main.cpp, following code solved the issue.
Queue allQueues[4];
allQueues[0] = queue1;
allQueues[1] = queue2;
allQueues[2] = queue3;
allQueues[3] = queue4;
To access:
for(int i=0; i<4; i++){
if allQueues[i].empty(){
//do something
}
}
If you need to generate a specific number of instances of your Queue class that is fixed and known at compile time, your code solution will work. However, if you have a program where new Queue instances need to be created while the program is running, you need to use dynamic memory allocation on the heap.
One approach to this is to create an array or a vector of pointers to your Queue class in main.cpp. The std::vector is more flexible, and it's best to use a smart pointer to create each instance of Queue, although many academic courses won't allow use of the standard template library or of smart pointers, and in that case you need just a normal array of pointers to Queue and use new and delete appropriately.
const int SIZE = 100 //max number of Queue instances
Queue* allQueues[SIZE]; //array of uninitialized pointers to Queue
for (int i = 0; i < SIZE; i++) { //ensure all pointers are set to null
allQueues[i] = nullptr;
}
//To make a new Queue instance and insert it into the array:
allQueues[0] = new Queue();
//And when done with that Queue instance, to avoid memory leaks and dangling pointers:
delete allQueues[0];
allQueues[0] = nullptr;
(This is all much better done with std::array, or std::vector, and smart pointers).
Note also the memory usage, without this approach you have two full-sized instances of Queue for queue1, instead of the object itself and a pointer to that object. However, one can do the array of pointers thing using only automatic stack allocation as well, but in that case, you don't want to be creating new objects at runtime. For that, it's simple:
Queue* allQueues[4];
allQueues[0] = &queue1;
//etc.
P.S. One problem with your solution is that when you do this assignment:
allQueues[0] = queue1;
You need a copy constructor in your class, or an overloaded '=' operator, to ensure that all of queue1's internals are correctly copied over into the the array of Queue objects, and avoid all the 'shallow copy' issues.
Queue::Queue(const Queue& copySource) {
this->size = copysource.size;
this->Q = new int[copysource.size];
for (int i = 0; i < size; i++) {
this->Q[i] = copysource.Q[i];
}
See:
Why can I access private variables in the copy constructor?

try-catch exception in c++

I was asked to use try-catch exception handling to handle the case if the list of students is empty. I'm new to exception handling can someone guide me? Below is the implementation of average method. I need to use try-catch to make sure the list isn't empty.
double classAverage()
{
int count = 0;
double sum = 0.0;
Container *temp = list;
if (temp == NULL)
{
cout << "List empty!";
return -1.0;
}
while (temp != NULL)
{
sum += temp->student->getGrade();
temp = temp->next;
count++;
}
return sum / count;
}
Since changeGrade is a friend of Student, this should work:
void changeGrade(Student* s, int newGrade) {
s->grade = newGrade;
}
Right now you repoint the pointer s to a new Student object. What you want
is to change the object that was passed in to the function.
There isn't enough information, but I think your code might look like this:
Student* student = new Student("John", "Doe", 99, 1);
changeGrade(student, 9);
This won't change the student at all, because you pass the pointer by value, so when you reassign it, the original object (the pointer student) is not changed. For the calling code to notice a change you have to pass by reference (Student*& - recommended way) or using a pointer to a pointer (Student**);. Also, don't forget to delete the original pointer (Student*) if you are using the Student** approach, or you'll leak memory.
You shouldn't make a way around not setting the int, its probably bad practice and confusing.

Not able to double the size of an array

I want to resize the array when the rehash function is called, by copying the values of initial dictionary into it and then at last redifining the newdictionary as dictionary
void rehash ()
{
int newsize=2*Size;
node **newdictionary;
newdictionary= new node*[newsize];
//Initialising the dictionary
for (int i = 0;i < newsize;i++)
{
newdictionary[i]->name = "";
newdictionary[i]->value = -1;
}
node **temp=dictionary;
delete [] dictionary;
dictionary=newdictionary;
SIZE=newsize;
for(int i=0;i<SIZE;i++)
{
if(temp[i]->value!= -1)
insertvalue(temp[i]->name,temp[i]->value);
}
delete [] temp;
};
Earlier I have defined insertvalue as:
void insertvalue (string filedata, int code)
{
// tableindex is the position where I want to insert the value
dictionary[tableindex]->name= filedata;
dictionary[tableindex]->value=code;
};
You didn't actually explain what problem(s) you're having, but your code has several issues:
void rehash ()
{
int newsize=2*Size;
node **newdictionary;
newdictionary= new node*[newsize];
At this point, newdictionary is simply an array of uninitialized pointers.
//Initialising the dictionary
for (int i = 0;i < newsize;i++)
{
newdictionary[i]->name = "";
newdictionary[i]->value = -1;
}
So the loop above is trying to access the members of node objects that don't yet exist.
node **temp=dictionary;
delete [] dictionary;
These two lines don't make sense. dictionary and temp point to the same memory. So when you delete dictinoary you've deleted the memory that temp is pointing to.
dictionary=newdictionary;
SIZE=newsize;
for(int i=0;i<SIZE;i++)
{
if(temp[i]->value!= -1)
insertvalue(temp[i]->name,temp[i]->value);
}
Even if you hadn't just deleted the memory out from under temp, you're now trying to access temp from 0 to the new size, not the old size. In other words, this would access temp beyond its bounds.
Those are the major problems that I've noticed in the code so far. You at least need to correct all of them before there's any hope of this working. You probably need to spend some time really stepping through your logic to ensure it makes sense in the end.

debug assertion failed: dbgheap.cpp and dbgdell.cpp

I have a some kind of memory leak problem. I had it in earlier lines, but i corrected it by writing a copy assignment constructor. But the problem is on the delete newB line. When i comment out that line, there is another error popping out. Where do you think i have some memory leaks because i know it is somehow related with the memory allocation.
void BankingSystem::addBranch(const int id, const string name){
if(isBranchExisting(id)){
cout << "\n\tBranch " << id << " already exists. Please try it with another id number.";
}
else if(!isBranchExisting(id)){
Branch* tempArray = new Branch[cntBranches];
if(cntBranches != 0){
for(int i = 0; i<cntBranches; i++){
tempArray[i] = allBranches[i];
}
delete[] allBranches;
allBranches = new Branch[cntBranches+1];
for(int i = 0; i<cntBranches; i++){
allBranches[i] = tempArray[i];
}
allBranches[cntBranches] = Branch(id, name);
delete[] tempArray;
}
Branch* newB = new Branch(id,name);
allBranches[cntBranches] = *newB;
cout << "\n\tBranch " << id << " is added successfully.";
delete newB;
cntBranches++;
}
}
I can show you the Branch class too if you need it because it may be related with constructors or destructor too, but i was not successful at correcting those as this error continues to pop out.
Edit: Sorry, i thought i stated it.
It fails because initially cntBranches==0 and allBranches is uninitialised, as I assume.
Thus when first time call addBranch
allBranches[cntBranches] = *newB;
will write into some random memory location pointed by garbage in allBranches.
Thru rest of pointer manipulations are incorrect and will lead to errors as well. For example delete[] tempArray; will delete everything that was allocated previously leaving allBranches pointing to deleted objects. So I'd suggest either read more about what pointer is and how mem allocation works, or use std::vector if possible.