从启动C ++的函数中删除动态分配的数组

时间:2014-11-07 08:02:59

标签: c++ arrays pointers struct dynamic-memory-allocation

我用C ++编写程序,必须使用来自各种结构的动态分配数组(在单独的文件中)。很多时候,我需要在函数内部启动这些数组。通常在启动它们之后,我将数据写入结构数组,甚至是结构数组内的数组,然后再使用数据。因此,我不会在函数内部使用delete。

例如,这是我使用的结构之一,即学生结构:

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
};

这是输入原始分数的功能。

// 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];
    }
}

在我的驱动程序文件中,学生也会使用值进行初始化。如图所示:

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

问题在于我使用这些原始分数和学生数组在单独的文件中进行计算,并将其用于其他文件中的输出,以及其他方法。如何删除这些中的任何一个?

此外,我意识到使用delete将删除结构内部的结构和指针,但不会删除指针指向的对象。因此,我假设这与第一个问题有关,我不能在我的计划结束时发出delete

编辑:对不起,正如许多其他人所指出的,我应该说明我对项目的限制。我不允许在结构体内部使用类,向量,函数(比如构造函数,析构函数)。

4 个答案:

答案 0 :(得分:4)

不要那样做。

您的系统设计糟糕,struct应为class并在内部处理rawScores内存 - 使用std::vector将是最简单的部分,但即使你使用常规指针,关键是应该在class中跟踪有关它们的数量和存储位置的信息。

换句话说,student结构应该跟踪有多少元素,并根据需要分配/释放内存。它不应该在函数inputRawScores中完成 - 该函数可以调用setNumRawScores的函数并调用setRawScore(n, value)的函数,但不在读取器函数中分配内存。它属于student结构中的成员函数。然后为您的student引入析构函数方法,该方法负责释放内存。

当然,使用std::vector将“隐藏”所有这些内容,您只需设置大小(或使用push_back)。

答案 1 :(得分:1)

鉴于你新发布的约束,我认为你可以实现一个删除函数来遍历Student数组并进行手动清理。

首先,我们创建一个删除一个单个学生的动态对象的函数。请注意,我们可以使用Student&作为参数类型,但是根据您的问题中的信息,我不确定您是否已经学习了引用,或者您是否被允许使用它们。所以我们坚持使用指针:

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;
}

之后我们创建一个函数,允许您通过循环遍历数组中的所有项并调用清理函数来删除完整的Student数组:

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

    delete[] students;
}

请注意我们获取指向对象的指针所需的&符号(&students[i])(作为清理函数的参数需要)。之后,学生数组本身将被删除。

你可以这样调用这些函数:

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

或者只有一个学生:

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

您可能已经注意到我们有时使用delete,有时使用delete[]。第一个只是释放已用new分配的内存。后者对已用new[]分配的内存执行相同的操作。这是非常重要的,否则你将得到运行时错误。另外,请始终确保结构中的 EVERY 指针已由NULL(C)或nullptr(C ++)初始化。

由于您似乎只是学习C / C ++,因此提及上述代码非常不安全至关重要,并且您可能遇到实际问题,例如: studentCount与数组中的实际项目数不匹配。但是现在我想你不会知道(或者不允许)做得更好。

编辑:我注意到您的finalGrade成员属于std::string*类型。这是指针的原因吗?因为如果你只想存储一个字符串,你可以只做一个std::string,没有理由有一个指针。请不要将char*类型的C字符串与STL字符串std::string混淆。

答案 2 :(得分:0)

在这种情况下,当你传递指向其他模块/函数的指针时,有效地删除你使用new分配的所有东西将是非常繁琐的。

这里有两个选项: -

  1. 将这些动态数组(rawscores / finalGrade)替换为相应的向量。

  2. 您可以使用智能指针(我认为如果您不使用容器,auto_ptr就足够了)来处理内存管理。

  3. 编辑: -

    如果要创建原始学生指针,则必须处理的一个主要问题是所有权。假设您已将内存分配给Student *,然后将此对象传递给多个模块/函数。您需要考虑到,当您的指针仍在某些其他模块中使用时,您不会调用delete。此外,它也应该在某些模块中已删除时调用delete。这就是我向你指出两个选择的原因......

答案 3 :(得分:0)

这个答案已经过时,因为这个问题的所有者指定了一些不允许构造函数/析构函数的约束。

就我解开你的问题而言,你不知道如何删除rawScores对象中实际动态分配的finalGradStudent对象,对吧?如果是这样,答案很简单,你只需使用析构函数:

struct Student {
    int id; // student ID
    char gradeOption; // either G or P
    double totalScore;
    std::string studentName;
    int* rawScores = nullptr;
    std::string* finalGrade = nullptr;

    // Disable copy and move assignment
    Student& operator=(const Student& rhs) = delete;
    Student& operator=(Student&& rhs) = delete;

    // Disable copy and move construction
    Student(const Student& rhs) = delete;
    Student(Student&& rhs) = delete;

    Student() {
        // Initialize members.
    }

    // Destructor
    ~Student() {
       delete[] rawScores;
       delete finalGrade;
   }
};

只要您使用Student运算符解除分配delete对象:

Student* students = new Student[numOfStudents];
delete[] students; // <-- deallocation

为每个对象调用析构函数,然后删除内部动态分配的对象。

如果指针是NULL,您不必费心,在delete的指针上调用NULL是有效的。另外,在构造对象时,请用nullptr初始化所有指针(如上所示)。

只要您没有混合不同EXE / DLL边界的运行时,您就可以从程序中的任何位置delete Student数组,无论您在哪个文件中分配/解除分配它。

我希望这能回答你的问题。如果没有,请更具体地说明您遇到的问题。

编辑:正如评论中所指出的,如果你这样做(或实现它们),你应该禁用复制/移动语义。但禁用它们实际上会使您动态分配Student结构,并使大多数STL容器的灵活性降低。否则,您也可以使用std::vector而不是动态分配的数组,如其他调查中所述。