You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:55:43