c++双精度释放或损坏(out):即使使用复制构造函数和赋值操作符

C++ double free or corruption (out): Even with copy constructor and assignment operator

本文关键字:复制 构造函数 赋值操作符 释放 双精度 损坏 out c++      更新时间:2023-10-16

我的代码正在生成*错误在' ./a。out': double free or corruption (out): 0x00007ffe400eb0e0 *

每当它运行时,我假设这是一个基于我的复制构造函数或如何删除我的动态数组的问题,但我有麻烦找出问题出现的地方:

我的类:

class Student {
  public:
    Student();
    Student(const Student&);
    Student & operator= (const Student&);
    ~Student();
    void setStudentData(int *, int &, int, string);
    int getNumOfSubjTaken();
    int getAverageMark();
    int getLowestMark();
    int getHighestMark();
    string getFullName();
    void sortMarks(int &);
  private:
    string fullName;
    int *marks;
    int numOfSubjects;
};

拷贝构造函数:

Student::Student(const Student& pupil) {
  marks = new int[numOfSubjects = pupil.numOfSubjects];
  for (int i = 0; i < numOfSubjects; i++) {
   marks[i] = pupil.marks[i];
  }
  fullName = pupil.fullName;
  //removed after edit: marks = pupil.marks;
}

赋值运算符:

Student &Student::operator=(const Student &pupil) {
 if (this != &pupil) {
   for (int i = 0; i < numOfSubjects; i++) {
     marks[i] = pupil.marks[i];
     }
   fullName = pupil.fullName;
    numOfSubjects = pupil.numOfSubjects;
    marks = pupil.marks;
 }
  return *this;

}

拆解:

Student::~Student(){
 if (marks != NULL) {
   delete [] marks;
 }
  marks = NULL;
  numOfSubjects = 0;
  fullName = "";
}

Set Function (Mutator):

 void Student::setStudentData(int *markArray, int &numStudents, int numSub, string fullName) {
  marks = new int[numSub];
  for (int i = 0; i < numSub; i++) {
    marks[i] = markArray[i];
  }
   this->numOfSubjects = numSub;
   this->fullName = fullName;
}

只有在我实现了我的写函数之后,这个问题才发生:

void writeFile(fstream &fout, char *argv[], Student *pupil, int &numRecs)        {
  const char sep = ' ';
  const int nameWidth = 5;
  const int numWidth = 7;
  fout.open(argv[2]);
 if (!fout.good()) {
    cout << "Error: Invalid data in " << argv[1] << " file." << endl;
    cout << "The program is terminated.";
    exit(EXIT_FAILURE);
  }
  else {
    // creating the table output
    fout << left << setw(nameWidth) << setfill(sep) << "Full Name";
    fout << left << setw(numWidth) << setfill(sep) << "mark1";
    fout << left << setw(numWidth) << setfill(sep) << "mark2";
    fout << left << setw(numWidth) << setfill(sep) << "mark3";
    fout << left << setw(numWidth) << setfill(sep) << "mark4";
    fout << left << setw(numWidth) << setfill(sep) << "average";
    fout << left << setw(numWidth) << setfill(sep) << "min";
    fout << left << setw(numWidth) << setfill(sep) << "max";
    fout << endl;
    for (int i = 0; i < numRecs; i++) { //numRecs being number of records/students
      fout << left << setw(nameWidth) << setfill(sep) << pupil[i].getFullName();
      for (int j = 0; j < pupil[i].getNumOfSubjTaken(); j++) { //writes each mark up to
        //fout << left << setw(numWidth) << setfill(sep) << pupil[i].marks[j];
    //This line doesn't work, but i need to be able to write the marks.
  }
  if (pupil[i].getNumOfSubjTaken() < 4) {
    for (int k = pupil[i].getNumOfSubjTaken(); k != 4; k++) {
      fout << left << setw(numWidth) << setfill(sep) << "  ";
    }
  }
  fout << left << setw(numWidth) << setfill(sep) << pupil[i].getAverageMark();
  fout << left << setw(numWidth) << setfill(sep) << pupil[i].getLowestMark();
  fout << left << setw(numWidth) << setfill(sep) << pupil[i].getHighestMark();
  fout << endl;
}
  }
}

我似乎也无法自拔<<学生[我].marks [j];即使它应该工作。

感谢您的时间和帮助。

您的赋值操作符是不正确的,因为它只做marks指针的浅拷贝。因此,您将得到一个双重自由错误,因为两个对象(thispupil)中的marks指针在对这些对象调用析构函数时将指向相同的内存。

注意,如果你使用std::vector<int> marks;而不是int *marks;,那么就不需要复制构造函数、赋值操作符或析构函数,因为std::vector<int>基本上完成了你在复制构造函数、赋值操作符和析构函数中试图做的事情。不同之处在于std::vector<int>安全、高效、无错误地完成了这一操作。

话虽如此,对代码的修复(不是唯一可能的修复)将是分配与传入的Student对象的主题数量匹配的新内存,释放marks内存,然后将marks分配给带有复制数据的新分配的内存。

Student &Student::operator=(const Student &pupil) 
{
   if (this != &pupil) 
   {
       // allocate new memory and copy
       int *temp = new int [pupil.numOfSubjects];
       for (int i = 0; i < pupil.numOfSubjects; i++) 
           temp[i] = pupil.marks[i];
       // deallocate old memory and assign
       delete [] marks;
       marks = temp;
       fullName = pupil.fullName;
       numOfSubjects = pupil.numOfSubjects;
   }
   return *this;
}

作为上述代码的替代,因为您似乎有一个工作的复制构造函数和析构函数(以下代码正确工作所必需的),一个更简单的解决方案是使用复制/交换习惯用法。

#include <algorithm>
//...
Student &Student::operator=(const Student &pupil) 
{
   Student temp(pupil);
   std::swap(temp.numOfSubjects, numOfSubjects);
   std::swap(temp.marks, marks);
   std::swap(temp.fullName, fullName);
   return *this;
}

使用复制构造函数和析构函数创建临时对象,然后用临时对象的内部结构替换this的内部结构。然后临时对象就会随着旧的内部结构一起消亡。

如果您想要一个可以调整大小的数组,STL已经提供了一个。您可以使用std::vector<int>来代替自己操作内存。


我读了你的复制构造函数,它似乎很好。同时,我认为你还应该创建一个move构造函数。


在你的赋值操作符中:

for (int i = 0; i < numOfSubjects; i++) {
    marks[i] = pupil.marks[i];
}

fullName =小学生.fullName;nummofsubjects = student . nummofsubjects;

  • 你没有分配this->mark。如果pupil.marksthis->marks的大小不一样呢?
  • 循环不能复制pupil.marks的全部内容,只能复制this->marks所能容纳的部分。
  • 你把东西复制到this->marks(这很好,一个深拷贝),但然后你做marks = pupil.marks
    • 深拷贝变为冗余
    • 它使thispupil"拥有"相同的marks,这意味着两者都会尝试在析构函数中释放它,导致你的问题。
    • 这一行引入了内存泄漏。

在析构函数中:
if (marks != NULL) {
    delete [] marks;
}
marks = NULL;
numOfSubjects = 0;
fullName = "";
  • 比较指针到NULL是一种C的做事方式。它基本上是一个通常等于0的宏。比较nullptr更正确。
  • 没有必要设置marksnullptr后,你已经释放了它。分配给其他成员也没有意义。

在您的肉函数:

for (int i = 0; i < numRecs; i++) {
    //numRecs being number of records/students
    fout << left << setw(nameWidth) << setfill(sep) << pupil[i].getFullName();
    for (int j = 0; j < pupil[i].getNumOfSubjTaken(); j++) {
        fout << left << setw(numWidth) << setfill(sep) << pupil[i].marks[j];
    }
}
  • 如果numRecs大于pupil数组的大小,则会出现溢出

所有这些点都是bug产生的原因。