Оказывается, у меня не было оператора присваивания копий, определенного в моем пользовательском классе, и поэтому компилятор по умолчанию "копировал поведение указателя объекта".
Если вы смущены тем, что такое оператор присваивания копий, как и я, этот ресурс может помочь вам разобраться, как выглядит "оператор присваивания копий" в C++.
Ниже приведено исходное задание. (Ссылки на исходный код истекают через месяц, извините!)
Я работаю над консольным приложением, которое имитирует книжный магазин, но продолжайте получать сообщение об ошибке _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
в моей программе во время выполнения. После некоторого поиска (и некоторой отрывочной отладки) я пришел к выводу, что мой деструктор объекта вызывается дважды. Я надеюсь, что нижеприведенные фрагменты кода сделают это немного яснее, что я имею в виду. (Нажатие на имена файлов откроет пасты с соответствующим кодом для упрощения чтения/декларации.)
#ifndef BOOKDATA_H
#define BOOKDATA_H
class bookData {
private:
char* bookTitle;
char* isbn;
char* author;
char* publisher;
char* dateAdded;
int qtyOnHand;
double wholesale;
double retail;
public:
bookData();
bookData(char* title, char* isbn, char* author, char* publisher, char* date, int qty, double wholesale, double retail);
bookData(bookData& book); // Meant to be called during memberwise assignment
~bookData();
// Various setter & getter funcs
};
#endif
#include "globals.h"
#include "bookData.h"
using namespace std;
// Variables in caps are const ints defined in globals.h
bookData::bookData() {
bookTitle = new char[TITLE_LENGTH];
isbn = new char[ISBN_LENGTH];
author = new char[AUTHOR_LENGTH];
publisher = new char[PUBLISHER_LENGTH];
dateAdded = new char[DATE_LENGTH];
qtyOnHand = 0;
wholesale = 0;
retail = 0;
char emptyTitle[2];
emptyTitle[0] = '\0';
setTitle(emptyTitle);
}
// Other constructors are overloaded version of bookData & copy data;
// See example setter function below destructor
bookData::~bookData() {
if (bookTitle)
delete [] bookTitle;
else
return;
delete [] isbn;
delete [] author;
delete [] publisher;
delete [] dateAdded;
}
// Setter functions are of this form (excl. ints & doubles)
void bookData::setTitle(const char* input) {
for (int len = 0; len < TITLE_LENGTH - 1; len++) {
*(bookTitle + len) = *(input + len);
if (*(input + len) == '\0')
break;
else if (len == TITLE_LENGTH - 2)
*(bookTitle + ++len) = '\0';
}
}
// Getter functions are of this form (excl. ints & doubles)
const char* bookData::getTitle() { return bookTitle; }
void repQty() {
// Again, variables in all caps are defined in globals.h if you don't see
// their declaration
bookData bookArray[MAX_RECORDS];
// Global function which populates bookArray from a datafile
bookData* books = getBooks(bookArray);
// Some code to find the memory address of the first and last book in the records
bookData* HEAD = books;
// Keep advancing until books no longer points to a non-empty bookData object
// "Empty" defined as book bookTitle variable starting with '\0'
bookData* TAIL = --books;
// Need all 3 pointers for a naive, in place insertion/linear sort routine
// Outputs book data following the sort
// Before returning, calls the destructors for the books in bookArray
// Also calls the destructor for books, HEAD, and TAIL as well
// ...which were already called as part of the bookArray destructor calls
// Which is where I have my problem now
}
Как вы могли заметить, я уже попытался проверить, был ли объект bookData
уже удален, используя if (bookTitle)
в функции деструктора, но он по-прежнему оценивается как истинный, когда я запускаю его через VS Step Into. Если не считать деструктора все вместе, что я могу сделать, чтобы обойти эту проблему и заставить деструктор преждевременно выйти, если объект, о котором идет речь, уже освобожден?
Я уже пытался проверить, был ли объект
bookData
уже удален, используя if (bookTitle
) в функции деструктора
Поскольку delete[]
не устанавливает указатель на NULL
, проверка фактически является no-op.
Даже если вы указали указатель на NULL
вручную, вы должны устранить симптомы проблемы, а не основную причину.
Основная причина заключается в том, что вы не выполняете оператор присваивания копий, тем самым нарушая правило из трех. Случается, что вы используете неявно сгенерированный оператор присваивания копии:
swap = *books;
*books = *(books - 1);
*(books - 1) = swap;
и этот оператор не делает правильные вещи: он копирует указатели вместо копирования данных. Прямым следствием этого являются двойные удаления.
Кроме того, реализация конструктора копирования может быть ошибочной, но трудно быть уверенным, не видя его исходного кода.
PS Вы сделали бы большую пользу, используя std::string
вместо строк C. Кроме того, std::vector
является предпочтительным для массивов C.
delete
только изменяетbookTitle
памятиbookTitle
на случайный», а затем настоятельно рекомендую не устанавливать вручную новый адрес памятиbookTitle
вNULL
Я в растерянности из-за того, что мне следует делать - я уже искал 2-3 часа и не могу понять.