2016-08-25 89 views
2

我的代码是生产 *错误的`./a.out“:双重释放或腐败(出):0x00007ffe400eb0e0 *C++双重释放或腐败(出):即使有拷贝构造函数和赋值运算符

每当它的运行,我认为这是基于要么我的拷贝构造函数的问题,或者我如何删除我的动态数组,但我无法工作哪里出了问题就出现了:

我的班级:

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 = ""; 
} 

设定功能(赋值函数):

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 < <瞳孔[I] .marks [J]。即使它应该工作。

感谢您的时间和帮助。

+1

析构函数在哪里?另外,由于您无法取消分配先前的内存(并重新分配新内存),您的赋值运算符会发生内存泄漏。但是,为什么所有这些在赋值运算符中工作时,只需执行[copy/swap](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom)? – PaulMcKenzie

+0

另外,你的析构函数可以简单的是'Student ::〜Student(){delete [] marks;}'。没有必要检查NULL,将值分配给被销毁的对象的成员是没有意义的。 – PaulMcKenzie

+0

'marks = pupil.marks;'在赋值运算符中是错误的。 'setStudentData'泄漏任何旧数据,并且不设置'numOfSubjects',并且不使用其参数的2个 –

回答

4

您的赋值运算符不正确,因为它只执行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; 
} 

提供替换上面的代码,因为你似乎有一个工作拷贝构造函数和析构函数(需要以下才能正常工作),一个简单的解决方案是使用copy/swap idiom

这是利用了拷贝构造函数和析构函数创建一个临时对象,然后换出的this的内部与临时对象的内部。然后这个临时对象就会随着老内部而死去。

+0

非常感谢您的详细解释和快速响应!不幸的是,即使我改变了复制构造函数之后,仍然返回了相同的错误。 – Mako

+0

然后你需要发布[mcve]。你的'setStudentData'函数没有发布,如果这也是错误的,我不会感到惊讶。但是为什么你的'Student'类没有一个构造函数来获取学生数据呢?无论如何,给出的答案需要做,以解决复制和分配“学生”课程的任何问题。 – PaulMcKenzie

0

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


我读过你的拷贝构造函数,看起来很好。 虽然我们在这,但我认为你也应该做一个移动构造函数。


在你的赋值操作符:

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

全名= pupil.fullName; numOfSubjects = pupil.numOfSubjects; marks = pupil.marks;

  • 您不分配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相比,它更为正确。
  • 在您取消分配后,您无需将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]; 
    } 
} 
  • 如果numRecspupil阵列的尺寸越大,你就会得到一个溢出。

所有这些点都是错误(S)的原因。

+1

*“与'nullptr'比较更为正确。”* - 可以说,不要将指针与nullptr进行比较更为正确。在'nullptr'上调用'operator delete []'是安全的,这会导致无操作。 – IInspectable