test1 = "there"
совпадает с test1 = String("there")
, потому что строка "there"
должна быть преобразована в String
. Оператор назначения по умолчанию работает, назначая все элементы с правой стороны элементам с левой стороны. Он делает поверхностную копию всех элементов, поэтому ваши строки на самом деле не дублируются, а оба указателя buffer
в test1
и String("there")
указывают на одну и ту же область памяти, в которой хранится строка "there"
.
В конце назначения объект String("there")
должен быть уничтожен, поэтому он вызывает деструктор, который delete
хранит память, на которую указывает его buffer
. В конце метода main()
то же самое происходит с test1
, только когда он попадает туда, он обнаруживает, что память уже была удалена, вызывая ошибку времени выполнения.
При работе с необработанными указателями в качестве класса члены, в дополнение к созданию собственного конструктора копирования вы также должны сделать свой собственный оператор назначения копирования, чтобы удалить уже сохраненную память и выделить новую память. Проще говоря, это может выглядеть так:
String& operator=(String const& other) {
delete[] buffer;
buffer = new char[other.size + 1];
strcpy(buffer, other.buffer);
size = other.size;
return *this;
}
Теперь присвоение test1
выделит полностью отдельное пространство памяти для хранения строки.