我用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
。
答案 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分配的所有东西将是非常繁琐的。
这里有两个选项: -
将这些动态数组(rawscores / finalGrade)替换为相应的向量。
您可以使用智能指针(我认为如果您不使用容器,auto_ptr就足够了)来处理内存管理。
编辑: -
如果要创建原始学生指针,则必须处理的一个主要问题是所有权。假设您已将内存分配给Student *,然后将此对象传递给多个模块/函数。您需要考虑到,当您的指针仍在某些其他模块中使用时,您不会调用delete。此外,它也应该在某些模块中已删除时调用delete。这就是我向你指出两个选择的原因......
答案 3 :(得分:0)
这个答案已经过时,因为这个问题的所有者指定了一些不允许构造函数/析构函数的约束。
就我解开你的问题而言,你不知道如何删除rawScores
对象中实际动态分配的finalGrad
和Student
对象,对吧?如果是这样,答案很简单,你只需使用析构函数:
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
而不是动态分配的数组,如其他调查中所述。