Краткий ответ:
Оператор str = String("Hi");
вызывает стандартный оператор copy-assignment (неявно объявленный) специальной функции-члена вашего класса. При уничтожении (в вашей программе создаются два объекта) одна и та же память будет уничтожена дважды, поскольку два объекта в вашей программе указывают на одну и ту же память, и оба они считают себя владельцем памяти.
Длинный ответ:
Поскольку вы еще не определили оператор копирования, и сгенерированная компилятором версия будет копировать указатели (shallow copy
) на другой класс.
Если для типа класса (структура, класс или объединение) не заданы пользовательские операторы назначения копирования, компилятор всегда объявляет их как встроенный общедоступный c член класса.
Объявленный имплицитом оператор присваивания копии, сгенерированный компилятором, будет выглядеть следующим образом:
String& operator=(const String& other)
{
strData = other.strData;
len = other.len;
return *this;
}
Итак, неявно объявленная функция-член оператора копирования присваивает мелкую копию и следовательно, он просто копирует указатели (а не их значение). До конца оператора str = String("Hi");
у вас будет два String
объекта (str
объект и временный объект, созданный String("Hi")
), и их strData
будут указывать на один и тот же массив символов. Когда временный файл, сгенерированный String("Hi")
, будет уничтожен, он освободит память, а когда объект str
будет уничтожен, он также попытается освободить / удалить ту же самую память и, следовательно, вы получите heap
поврежденным.
Подробнее о копирование и замена идиома. Попробуйте это:
String& operator=(String other)
{
swap(other, *this);
return *this;
}
void swap(String& first, String& second)
{
std::swap(first.strData, second.strData);
std::swap(first.len, second.len);
}
Попробуйте программу ниже. Я прокомментировал оператор copy-assignment
, чтобы продемонстрировать уничтожение указателя дважды.
#include <iostream>
#include <string.h>
class String {
public :
String(void): strData(nullptr), len(0)
{
std::cout << "constructor executed !" << std::endl;
}
String(const char *str)
{
len = strlen(str);
strData = new char[len + 1];
std::copy(str, str + len + 1, strData);
std::cout << "Constructing memory: " << strData << std::endl;
std::cout << sizeof(str) << std::endl;
std::cout << sizeof(len) << std::endl;
std::cout << sizeof(strData) << std::endl;
std::cout << strData << std::endl;
}
// String& operator=(String other)
// {
// swap(other, *this);
// return *this;
// }
~String()
{
if (strData == nullptr)
return;
std::cout << "Destroying memory: " << (void *)strData << std::endl;
delete[] strData; //Fails here!
}
char* GetStrData(void) const
{
return strData;
}
int GetLen(void) const
{
return len;
}
void swap(String& first, String& second)
{
std::swap(first.strData, second.strData);
std::swap(first.len, second.len);
}
private :
char* strData;
int len;
};
int main()
{
String str;
str = String("Hi");
return 0;
}
Вывод:
constructor executed !
Constructing memory: Hi
8
4
8
Hi
Destroying memory: 0x7fffc5628080
Destroying memory: 0x7fffc5628080