free(): двойное свободное обнаружено в tcache 2 при вызове перегруженного оператора назначения

#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; }