#c #free #delete-operator #exit-code #double-free
Вопрос:
Я работаю над университетским проектом, в котором мы должны реализовать часть класса строк c в качестве Mystring. Я работаю над перегруженным оператором присваивания, и это текущий код для него:
Mystringamp; Mystring::operator=(const Mystringamp; orig) { if(this != amp;orig) { delete ptr_buffer; len = orig.len; buf_size = orig.buf_size; ptr_buffer = orig.ptr_buffer; } return *this;
}
ptr_buffer, лен. и buf_size-это три частные переменные для класса Mystring. Это моя основная программа для тестирования:
void check (const Mystring s, const string name) { cout lt;lt; "checking " lt;lt; name lt;lt; endl; cout lt;lt; name lt;lt; " contains " lt;lt; s lt;lt; endl; cout lt;lt; name lt;lt; " capacity() is " lt;lt; s.capacity() lt;lt; endl; cout lt;lt; name lt;lt; " length() is " lt;lt; s.length() lt;lt; endl; cout lt;lt; name lt;lt; " size() is " lt;lt; s.size() lt;lt; endl; cout lt;lt; name lt;lt; " max_size() is " lt;lt; s.max_size() lt;lt; endl lt;lt; endl; } int main() { Mystring s1("Hi there!"); check(s1, "s1"); Mystring s2("Testing before assignment!"); check(s2, "s2"); s2 = s1; check(s2, "s2"); return 0; }
Вот что это выводит:
checking s1 s1 contains Hi there! s1 capacity() is 10 s1 length() is 9 s1 size() is 9 s1 max_size() is 1073741820 checking s2 s2 contains Testing before assignment! s2 capacity() is 27 s2 length() is 26 s2 size() is 26 s2 max_size() is 1073741820 checking s2 s2 contains Hi there! s2 capacity() is 10 s2 length() is 9 s2 size() is 9 s2 max_size() is 1073741820 free(): double free detected in tcache 2 Process finished with exit code 134 (interrupted by signal 6: SIGABRT)
Как вы можете видеть, назначение ДЕЙСТВИТЕЛЬНО работает для установки всех переменных-членов, однако я получаю ненулевой код выхода и ошибку free(): double free, обнаруженную в tcache 2. Что я делаю не так и что означает эта ошибка? Я подтвердил, что код выхода получен при вызове задания, потому что, когда я закомментирую s2 = s1;, он завершается кодом выхода 0.
Комментарии:
1.
delete ptr_buffer;
должно бытьdelete[] ptr_buffer;
, если вы используетеnew[]
2. @Frank да, это было предоставлено нашим инструктором
3.
orig.ptr_buffer
все еще готово. Когдаorig
уничтожается, предположительно, его деструктор удаляет его4. У вас есть 2 указателя на один и тот же буфер после копирования. Вам нужно
ptr_buffer = new char[buf_size];
и скопировать данные сorig.ptr_buffer
5. @ДрейкФорд Нет, это не работает. Это будет работать только в том случае, если у вас есть место в буфере. Сначала вам нужен
new[]
новый буфер, но подумайте об этом: вам нуженdelete[]
буфер только в том случае, если в нем нетcapacity
size
входаorig
. Если у вас уже есть возможности, неdelete[]
делайте этого , просто скопируйте изorig
— если вам это нужноdelete[]
, вам также нужноnew[orig.buf_size]
— и не забудьтеcapacity
соответствующим образом настроить.
Ответ №1:
В этом операторе присвоения копии
Mystringamp; Mystring::operator=(const Mystringamp; orig) { if(this != amp;orig) { delete ptr_buffer; len = orig.len; buf_size = orig.buf_size; ptr_buffer = orig.ptr_buffer; } return *this; }
есть по крайней мере две проблемы. Если массив символов, на который указывает указатель ptr_buffer
, был выделен динамически, то вам необходимо использовать оператор delete []
вместо delete
delete [] ptr_buffer;
Вторая проблема заключается в том, что после этого задания
ptr_buffer = orig.ptr_buffer;
два указателя указывают на одну и ту же динамически выделяемую память.
Вам нужно выделить новый экстент памяти и скопировать туда строку назначенного объекта.
Оператор может быть определен, по крайней мере, следующим образом
Mystring amp; Mystring::operator =( const Mystring amp;orig ) { if(this != amp;orig) { if ( buf_size != orig.buf_size ) { delete [] ptr_buffer; ptr_buffer = new char[orig.buf_size]; buf_size = orig.buf_size; } len = orig.len; strcpy( ptr_buffer, orig.ptr_buffer ); // or if the class does not store strings then // memcpy( ptr_buffer, orig.ptr_buffer, len ); } return *this; }
Комментарии:
1. придирка: комментарий должен гласить «не хранит строки, заканчивающиеся нулем «. Стандарт
std::string
допускает нули в середине строки просто отлично.
Ответ №2:
Предположительно, Mystring
имеет деструктор, который выглядит так:
Mystring::~Mystring() { delete[] ptr_buffer; }
Таким образом, вы не можете просто захватить собственность ptr_buffer
, orig
не заменив ее чем-то другим. Это возможно при задании на перемещение, но поскольку вы выполняете задание на копирование, вы должны оставить orig
все как есть.
Это означает, что вы должны скопировать orig
в него данные, выделив при необходимости новые:
Mystringamp; Mystring::operator=(const Mystringamp; orig) { if(this != amp;orig) { std::size_t min_buf_size = orig.len 1; // or perhaps orig.buf_size, it depends. // no need to allocate a new buffer if the current one is big enough already. if(buf_size lt; min_buf_size) { delete[] ptr_buffer; buf_size = min_buf_size; ptr_buffer = new char[buf_size]; } len = orig.len; assert(buf_size gt;= len 1); std::memcpy(ptr_buffer, orig.ptr_buffer, len 1); } return *this; }