2016-08-25 87 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