Search code examples
c++arrayspointersstructdynamic-memory-allocation

Deleting a dynamically allocated array from a function that initiates it C++


I'm writing a program in C++ that has to use dynamically allocated arrays from various structures (in separate files). A lot of times, I need to initiate these arrays inside of a function. Usually after initiating them, I write data to the array of a structure, or even an array inside of an array of structures, and then use the data later on. Therefore I don't use delete inside of the function.

For example, here is one of the structures that I use, a student structure:

struct Student {
    int id; // student ID
    char gradeOption; // either G or P
    double totalScore;
    std::string studentName;
    int* rawScores = NULL; // array that holds raw scores for a student
    // if no scores are entered for a specific ID, we check for NULL
    // we can then set the scores to 0
    std::string* finalGrade; // final grade given in course
};

And here is the function to input raw scores.

// input raw scores for each id
void inputRawScores(int gradedArtifacts, int id, Student* student) {
    student[id].rawScores = new int[gradedArtifacts];
    for(int i = 0; i < gradedArtifacts; i++) {
        std::cin >> student[id].rawScores[i];
    }
}

In my driver file, students also gets initialized with a value. Shown here:

 Student* students = new Student[numOfStudents]; // array of students

The problem is is that I use these raw scores, and the array of students for calculations in a separate file, and use them for output in other files, and in other methods. How would I go about deleting any of these?

Also I realize that using delete will delete the structure and the pointers inside of the structure, but not the objects that the pointers point to. So I'm assuming this ties back into the first question and I can't just issue a delete at the end of my program.

Edit: I'm sorry, as many others have pointed out I should have stated the restraints that I have on the project. I'm not allowed to uses: classes, vectors, functions inside of structs (like constructors, destructors).


Solution

  • Given your newly posted constraints, I think you could just implement a deletion-function to traverse through the Student array and do manual cleanup.

    First, we create a function that deletes the dynamic objects of one single student. Note that we could have used Student&as the parameter type, but given the information in your question I am not sure if you have learned references yet, or if you are allowed to use them. So we stick with the pointer:

    void cleanupStudent(Student* student) {
        delete[] student->rawScores;
        delete student->finalGrade;
    
        // EDIT: Setting pointers back to NULL after deletion provides
        // some improved safety and is good practice.
        student->rawScores = NULL;
        student->finalGrade = NULL;
    }
    

    After that we create a function that allows you to delete the complete Student array by looping through all the items in the array and calling the cleanup function:

    void deleteStudents(Student* students, int studentCount) {
        for(int i = 0; i < studentCount; i++) {
            cleanupStudent(&students[i]);
        }
    
        delete[] students;
    }
    

    Here, please note the ampersand-symbol (&students[i]) which we require to get a pointer to the object (which is required as a parameter to the cleanup function). After that, the student array itself is deleted.

    You can call these functions like this:

    int numOfStudents = 16;
    Student* students = new Student[numOfStudents];
    deleteStudents(students, numOfStudents);
    

    Or with a single student:

    Student* student = new Student;
    cleanupStudent(student);
    delete student;
    

    As you might have noticed we sometimes use delete and sometimes delete[]. The first one just deallocates memory that has been allocated with new. The latter does the same thing to memory that has been allocated with new[]. This is very important to get right, otherwise you will get runtime errors. Also, make always sure that EVERY pointer in your struct is initialized wih NULL (C) or nullptr (C++).

    Since it seems that you are just learning C/C++ it is crucial to mention that the above code is very unsafe, and you could get into real problems if e.g. the studentCount is not matching the actual number of items in the array. But for now I guess you wouldn't know (or aren't allowed) to do better.

    EDIT: I noticed that your finalGrade member is of type std::string*. Is there a reason for this to be a pointer? Because if you just want to store a string, you can just do a std::string, no reason there to be a pointer. Please don't confuse a C-String of type char* with a STL string std::string.