【问题标题】:C++ double free or corruption (out): Even with copy constructor and assignment operatorC++ 双重释放或损坏(出):即使使用复制构造函数和赋值运算符
【发布时间】:2023-03-07 23:59:01
【问题描述】:

我的代码正在生成 * `./a.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 = "";
}

设置函数(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;
}

  }
}

我也看不出来

感谢您的时间和帮助。

【问题讨论】:

  • 析构函数在哪里?此外,您的赋值运算符存在内存泄漏,因为您未能释放以前的内存(并重新分配新内存)。但是,当您可以简单地执行 copy / swap 时,为什么所有这些都可以在赋值运算符中工作?
  • 另外,你的析构函数可以是Student::~Student() {delete [] marks;}。无需检查 NULL,将值分配给正在被销毁的对象的成员是没有意义的。
  • 赋值运算符中的 marks = pupil.marks; 是错误的。 setStudentData 泄漏任何旧数据,并且不设置 numOfSubjects.,并且不使用它的 2 个参数

标签: c++ class copy-constructor assignment-operator


【解决方案1】:

如果您想要一个可以调整大小的数组,STL 已经提供了一个。您可以使用std::vector&lt;int&gt; 而不是自己操作内存。


我已经阅读了您的复制构造函数,它看起来不错。 当我们这样做时,我认为您还应该创建一个移动构造函数。


在你的赋值运算符中:

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

fullName = 瞳孔.fullName; numOfSubjects = 瞳孔.numOfSubjects; 标记=瞳孔.标记;

  • 你没有分配this-&gt;mark。如果pupil.marks 的大小与this-&gt;marks 不同怎么办?
  • 循环不能复制整个pupil.marks - 只能复制this-&gt;marks 可以容纳的量。
  • 您将内容复制到this-&gt;marks(这很好,深拷贝),然后您执行marks = pupil.marks
    • 深拷贝变得多余
    • 它使thispupil “拥有”相同的marks,这意味着两者都会尝试在它们的析构函数中释放它,从而导致您的问题。
    • 这一行引入了内存泄漏。

在你的析构函数中:

if (marks != NULL) {
    delete [] marks;
}
marks = NULL;
numOfSubjects = 0;
fullName = "";
  • 比较指向NULL 的指针是C 的一种处理方式。它基本上是一个通常等于 0 的宏。与nullptr 比较更正确。
  • 释放后无需将marks 设置为nullptr。也没有必要分配给其他成员。

在你的丰富功能中:

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 数组的大小,则会出现溢出。

所有这些都是导致错误的原因。

【讨论】:

  • “与nullptr 比较更正确。” - 可以说,不将指针与nullptr 比较更正确。在nullptr 上调用operator delete[] 是安全的,这会导致无操作。
【解决方案2】:

您的赋值运算符不正确,因为它只对marks 指针进行了浅拷贝。因此,当在这些对象上调用析构函数时,两个对象(thispupil)中的 marks 指针将指向相同的内存,因此您将收到双重释放错误。

请注意,如果您使用 std::vector&lt;int&gt; marks; 而不是 int *marks;,则不需要复制构造函数、赋值运算符或析构函数,因为 std::vector&lt;int&gt; 基本上可以完成您在复制构造函数、赋值运算符和析构函数。不同之处在于std::vector&lt;int&gt; 安全、高效且没有错误地执行此操作。

话虽如此,对代码的修复(不是唯一可能的修复)是分配与传入的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;
}

作为上述代码的替代方案,由于您似乎有一个有效的复制构造函数和析构函数(以下代码正常工作所必需的),一个更简单的解决方案是使用copy / swap idiom

#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 的内部结构替换为临时对象的内部结构。然后临时对象随着旧的内部结构而消失。

【讨论】:

  • 非常感谢您的详细解释和快速回复!不幸的是,即使我改变了复制构造函数,我仍然返回同样的错误。
  • 那你需要发一个minimal reproducible example。你的setStudentData 函数没有发布,如果这也是错误的,我不会感到惊讶。但是为什么你的Student 类没有获取学生数据的构造函数呢?无论如何,需要完成给出的答案以解决复制和分配 Student 类的任何问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多