C++ - 在统计分配的数组中打印对象会导致分段错误

C++ - printing objects in statistically allocated array causes segmentation fault

所以我正在创建一个程序来实现多个 classes 代表一所学校及其学生和课程。当我尝试打印出 studentCoursePairs[] 数组中的所有 Taken 对象时,我遇到了分段错误,该数组代表参加特定课程的 Student 对象。我认为我的分段错误来自 School.cc 中的 addTaken() 函数,它的工作是找到具有给定学号和课程 ID 的学生对象和课程对象,然后用找到的学生创建一个新的 Taken 对象和课程对象以及成绩。然后我尝试将这个新对象添加到 Taken 集合的后面,即 studentCoursePairs。

当我注释掉 studentCoursePairs[i]->print() 时,分段错误消失了。我不太确定我做错了什么,希望得到一些帮助。

我不确定除了 School.cc 之外是否还需要其他 classes,但我还是将它们包括在内以帮助理解。

School.cc:

#include <iostream>
#include <iomanip>
using namespace std;
#include <string.h> 

#include "School.h"

School::School(string s1) : name(s1){ 
    numTaken = 0;
}

void School::addTaken(string number, int code, string grade){
    Student* s = nullptr;
    Course* c = nullptr;
    for(int i = 0; i < numTaken; ++i){
        if((studentsCollection->find(number, &s)) && (coursesCollection->find(code, &c))){
        Taken* taken = new Taken(s, c, grade);
          studentCoursePairs[i] = taken;            
          ++numTaken;
        }
    }
}

void School::printTaken(){
    cout << name << " === TAKEN: "<< endl;
    for(int i = 0; i < sizeof(studentCoursePairs)/sizeof(studentCoursePairs[0]); ++i){
        studentCoursePairs[i]->print(); //seg fault
    }   
}

其他文件:

StudentCollection.cc

bool StudentCollection::find(string num, Student** s){
    for(int i = 0; i < size; ++i){
        if(students[i]->getNumber() == num){ //find student number
            *s = students[i];
        }
    }
}

CoursesCollection.cc

bool CoursesCollection::find(int id, Course** c){
    for(int i = 0; i < numCourses; ++i){
        if(courses[i]->getId() == id){ //find course id
            *c = courses[i];
        }
    }
}

我还有一个学生 class 和课程 class,它们只是声明和初始化信息,例如学生的姓名、课程、gpa 以及课程代码、讲师、姓名、年份一门课程。

您的 School 对象有两个主要问题。让我们从您在问题中发布的那个开始:

void School::printTaken(){
    cout << name << " === TAKEN: "<< endl;
    for(int i = 0; i < sizeof(studentCoursePairs)/sizeof(studentCoursePairs[0]); ++i){
        studentCoursePairs[i]->print(); //seg fault
    }   
}

这个 for 循环总是 运行 正好 MAX_PAIRS 次,因为这个变量被定义为

Taken* studentCoursePairs[MAX_PAIRS];

所以sizeof(studentCoursePairs) === MAX_PAIRS * sizeof(studentCoursePairs[0]).

相反,您只想循环实际包含有效指针的前几个槽。你有一个变量:numTaken。因此,将条件更改为 i < numTaken 并且您的打印循环将起作用。

第二个主要问题在addTaken:

void School::addTaken(string number, int code, string grade){
    Student* s = nullptr;
    Course* c = nullptr;
    for(int i = 0; i < numTaken; ++i){
        if((studentsCollection->find(number, &s)) && (coursesCollection->find(code, &c))){
        Taken* taken = new Taken(s, c, grade);
          studentCoursePairs[i] = taken;            
          ++numTaken;
        }
    }
}

让我们来玩玩电脑,看看如果输入的数字和代码有效会发生什么:

  • 如果 numTaken 为 0,则循环立即停止(因为 0 < 0 为假)并且 numTaken 不递增。 addTaken随便叫,永远不会变numTaken
  • 假设你解决了这个问题,让我们假设 numTaken = 5。在第一次迭代中,你检查条件并同意这是一个有效的 number-code 组合。因此,您创建了一个新的 Taken 对象并 .. 用新对象覆盖 studentCoursePairs[0]。在第二次迭代中,您执行相同的操作并用等效对象覆盖 studentCoursePairs[1]

这可能不是预期的行为。 相反,您可能想在 studentCoursePairs[numTaken] 中放置一个新对象并碰撞 numTaken:

void School::addTaken(string number, int code, string grade){
    Student* s = nullptr;
    Course* c = nullptr;
    if((studentsCollection->find(number, &s)) && (coursesCollection->find(code, &c))){
        Taken* taken = new Taken(s, c, grade);
        studentCoursePairs[numTaken] = taken;            
        ++numTaken;
    }
}

弄清楚如何处理传递的组合无效或超过 MAX_PAIRS 组合的情况留给您作为练习。

编辑:您的 CoursesCollection 中存在第三个主要问题:您为一个对象 new Course() 分配 space,同时将其视为数组,并将结果存储在局部变量中的一个成员。你的构造函数应该看起来像:

CoursesCollection::CoursesCollection(){
    courses = new Course*[MAX_COURSES];
    numCourses = 0;
}

或者,使用成员初始值设定项列表:

CoursesCollection::CoursesCollection() 
  : courses(new Course*[MAX_COURSES]), numCourses(0) {}