C++自定义排序函数导致程序崩溃及classList乱码问题求助
C++学生记录排序崩溃及classList乱码问题解决
我用C++实现从文件读取Student记录并将排序后的结果写入另一个文件的功能,但调用自定义的void sort(Student A[], int count)函数(传入newStudent和Student::getnStudents())时遇到两个问题:
- 当学生数量超过3时,程序无警告直接崩溃;
- 排序后部分Student对象的
classList[0]出现乱码。
注释掉sort函数调用后程序能正常运行,尝试过修改排序函数、调整重载的<运算符、将lname设为public直接比较等方法,都无法解决问题。相关代码如下:
main.cpp
#include <iostream> #include <fstream> //for input output files using namespace std; #include "student.h" #include "student.cpp" // Inputs students from a text file using "lname fname n (list of n classes)" and sorts them to a seperate text file. // Includes statistics about classes and the number of students taking each class. //================================================== void sort(Student A[], int count) { // Sort function // <- PROBLEM for (int i=1; i<count; i++) { for (int j=i; (j>0) && (A[j] < A[j-1]); j--) { swap(A[j],A[j-1]); } } } //================================================== int main(int argc, char *argv[]) { if (argc != 4) { cout << "Usage: " << argv[0] << " (input) (output) (# of elements)" << endl; exit(1); } // Variables from argv. input/output location and max elements for Student string inputFile = argv[1]; string outputFile = argv[2]; int maxStudents = atoi(argv[3]); // Array to calculate class statistics int numberClasses[6] = {0}; string classNames[6] = {"CSC121", "CSC127", "CSC275", "CSC375", "CSC377", "CSC381"}; // Array that is a copy of numberClasses int numberClasses2[6]; // Most and least common classes string mostCommon; string leastCommon; // Average number of classes per student float avgClasses = 0.0; // Student array Student *newStudent; newStudent = new Student[maxStudents]; //================================================== ifstream inStream; inStream.open(inputFile); for (int i=0; i<maxStudents; i++) { inStream>>newStudent[i]; } inStream.close(); //================================================== ofstream outStream; outStream.open(outputFile); for(int i=0; i<Student::getnStudents(); i++) { // TEST BEFORE cout<<newStudent[i]<<endl; } sort(newStudent, Student::getnStudents()); // <- THIS IS THE PROBLEM cout<<endl; for(int i=0; i<Student::getnStudents(); i++) { // TEST AFTER cout<<newStudent[i]<<endl; } for(int i=0; i<Student::getnStudents(); i++) { outStream<<newStudent[i]<<endl; } outStream.close(); //================================================== // Go through student array and their class list to increase the corresponding numberClasses array. EX: numberClasses[0] = classNames[0]. for(int i=0; i<Student::getnStudents(); i++) { for (int j=0; j<newStudent[i].getnclasses(); j++) { for (int k=0; k<6; k++) { if (newStudent[i].getclassList(j) == classNames[k]) {numberClasses[k]++;} } } } // loop to print statistics of number of students taking each class for (int i=0; i<6; i++) { cout<<endl<<"Number of students taking "<<classNames[i]<<": "<<numberClasses[i]; } //================================================== for (int i=0; i<6; i++) { numberClasses2[i] = numberClasses[i]; } // Sort numberClasses2 for (int i=0; i<6; i++) { for (int j=i; (j>0) && (numberClasses2[j] < numberClasses2[j-1]); j--) { swap(numberClasses2[j],numberClasses2[j-1]); } } // Compare numberClasses2 to numberClasses for (int i=0; i<6; i++) { if (numberClasses2[6-1] == numberClasses[i]) { mostCommon = classNames[i]; } if (numberClasses2[0] == numberClasses[i]) { leastCommon = classNames[i]; } } // Calculate average classes taken per student for (int i=0; i<Student::getnStudents(); i++) { avgClasses += newStudent[i].getnclasses(); } avgClasses = avgClasses/Student::getnStudents(); //================================================== // Print rest of statistics cout<<endl<<"Most common course: "<<mostCommon; cout<<endl<<"Least common course: "<<leastCommon; cout<<endl<<"Average number of courses taken, per student: "<<avgClasses; return 0; }
student.h
#ifndef STUDENT_H #define STUDENT_H class Student { public: Student(); // Constructor ~Student(); // Destructor // Accessors string getclassList(int idx) { return classList[idx]; } static int getnStudents(){return nStudents;} int getnclasses(){return nclasses;} // Overloaded I/O stream operators friend istream& operator>> (istream&, Student&); friend ostream& operator<< (ostream&, const Student&); // Overloaded compare friend bool operator < (const Student& S1, const Student& S2); private: string lname; // Person's last name string fname; // Person's first name int nclasses; // Number of courses enrolled in string *classList; // An array of strings for names of courses enrolled in // Static variables for # of students static int nStudents; }; int Student::nStudents = 0; #endif
student.cpp
#include "student.h" using namespace std; // Destructor Student::~Student() { delete[] classList; classList = NULL; } // Default constructor Student::Student() : fname("First"), lname("Last") { classList = new string[6]; } //================================================== // Overloaded input istream& operator >> (istream& inputStream, Student& S){ inputStream >> S.lname; inputStream >> S.fname; inputStream >> S.nclasses; for (int i=0; i<S.nclasses; i++) { inputStream >> S.classList[i]; } if (S.nclasses>0) {Student::nStudents++;} return inputStream; } // Overloaded output ostream& operator<< (ostream& outputStream, const Student& S){ outputStream<<S.lname<<" "<<S.fname<<" "<<S.nclasses; for (int i=0; i<S.nclasses; i++) { outputStream<<" "<<S.classList[i]; } return outputStream; } //================================================== // Overloaded compare bool operator < (const Student& S1, const Student& S2) { if (S1.lname == S2.lname) { return (S1.fname <= S2.fname); } return (S1.lname < S2.lname); }
问题根源
核心原因是Student类存在浅拷贝漏洞:
Student类包含动态分配的指针classList,但你没有显式实现拷贝构造函数和赋值运算符。C++默认生成的拷贝构造与赋值操作只会拷贝指针的内存地址,而非指针指向的实际内容。
当排序函数中调用swap(A[j], A[j-1])时,会触发默认赋值操作,导致两个Student对象的classList指向同一块内存。后续操作中,若其中一个对象被析构,会释放这块内存,另一个对象的classList就变成悬空指针,访问时会出现乱码,甚至触发内存错误导致程序崩溃。
解决方案
方案1:手动实现深拷贝(拷贝构造+赋值运算符)
修改student.h,添加拷贝构造和赋值运算符声明:
class Student { public: Student(); // 构造函数 Student(const Student& other); // 拷贝构造函数 ~Student(); // 析构函数 Student& operator=(const Student& other); // 赋值运算符重载 // 原有成员... };
在student.cpp中实现这两个函数:
// 拷贝构造函数 Student::Student(const Student& other) : fname(other.fname), lname(other.lname), nclasses(other.nclasses) { // 为新对象分配独立的classList内存 classList = new string[6]; for (int i = 0; i < nclasses; ++i) { classList[i] = other.classList[i]; } } // 赋值运算符重载 Student& Student::operator=(const Student& other) { if (this == &other) { // 防止自赋值 return *this; } // 释放当前对象的classList内存 delete[] classList; // 拷贝基本成员 fname = other.fname; lname = other.lname; nclasses = other.nclasses; // 分配新内存并拷贝课程列表 classList = new string[6]; for (int i = 0; i < nclasses; ++i) { classList[i] = other.classList[i]; } return *this; }
方案2:改用std::vector自动管理内存
用std::vector<string>替代动态分配的string* classList,标准库会自动处理拷贝与内存管理,无需手动实现拷贝构造和赋值运算符:
修改student.h中的成员:
private: string lname; string fname; int nclasses; vector<string> classList; // 替换原有的string* classList
修改构造函数和析构函数:
Student::~Student() { // vector会自动释放内存,无需手动操作 } Student::Student() : fname("First"), lname("Last"), nclasses(0) { // vector默认初始化即可 }
修改输入运算符:
istream& operator >> (istream& inputStream, Student& S){ inputStream >> S.lname; inputStream >> S.fname; inputStream >> S.nclasses; S.classList.clear(); // 清空原有内容 for (int i=0; i<S.nclasses; i++) { string cls; inputStream >> cls; S.classList.push_back(cls); } if (S.nclasses>0) {Student::nStudents++;} return inputStream; }
修改输出运算符:
ostream& operator<< (ostream& outputStream, const Student& S){ outputStream<<S.lname<<" "<<S.fname<<" "<<S.nclasses; for (const string& cls : S.classList) { outputStream<<" "<<cls; } return outputStream; }
修改访问函数:
string getclassList(int idx) { return classList[idx]; }
内容的提问来源于stack exchange,提问作者mvpnoscar
相关产品推荐
相关产品推荐

