C ++ double free或corruption(out):即使使用复制构造函数和赋值运算符

时间:2016-08-25 02:55:12

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

我的代码正在制作中 * ./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;
}

  }
}

我也似乎无法fout&lt;&lt;瞳孔[I] .marks [J];即使它应该工作。

感谢您的时间和帮助。

2 个答案:

答案 0 :(得分:4)

您的赋值运算符不正确,因为它只执行marks指针的浅表副本。因此,当在这些对象上调用析构函数时,两个对象(marksthis)中的pupil指针将指向同一内存,因此您将获得双重自由错误。 / p>

请注意,如果您使用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;
}

作为上述代码的替代,因为您似乎有一个工作副本构造函数和析构函数(以下工作正常所必需),更简单的解决方案是使用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的内部交换为临时对象的内部。然后临时对象与旧的内部结束。

答案 1 :(得分:0)

如果你想要一个可以调整大小的数组,STL已经提供了一个。您可以使用std::vector<int>而不是自己操纵记忆。

我已经阅读了你的拷贝构造函数,看起来很好。 虽然我们正在考虑它,但我认为你也应该做一个移动构造函数。

在你的作业中:

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

fullName = pupil.fullName;    numOfSubjects = pupil.numOfSubjects;    marks = pupil.marks;

  • 您没有分配this->mark。如果pupil.marks的大小与this->marks相同,那该怎么办?
  • 循环无法复制pupil.marks的全部内容 - 只有this->marks可以保留。
  • 您将内容复制到this->marks(这是一个很好的深层副本),然后您执行marks = pupil.marks
    • 深层复制变得多余
    • 它使thispupil&#34;拥有&#34;相同的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数组的大小,则会出现溢出。

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