Проблемы:
В вашем операторе присваивания вы не предоставляете никаких гарантий исключений. Вы удаляете буфер, прежде чем гарантируете, что операция будет успешной. Если что-то пойдет не так, ваш объект останется в неопределенном состоянии.
CString& operator = (const CString& rhs)
{
cout << "Assignment operator called!"<<endl;
if (this != &rhs)
{
len= rhs.len;
delete[] buff;
buff= new char[len+1]; /// BOOM
// If you throw here buff now points at undefined memory.
// If this is an automatic variable the destructor is still going
// to be called and you will get a double delete.
// All operations that can fail should be done BEFORE the object is modified.
strcpy_s(buff, len+1, rhs.buff);
}
return *this;
}
Мы можем исправить эти проблемы, перемещая вещи (и используя временную температуру).
CString& operator = (const CString& rhs)
{
cout << "Assignment operator called!"<<endl;
if (this != &rhs)
{
char* tmp = new char[len+1];
strcpy_s(tmp, rhs.len+1, rhs.buff); // for char this will never fail
// But if it was another type the copy
// may potentially fail. So you must
// do the copy before changing the curren
// objects state.
// Now we can change the state of the object safely.
len= rhs.len;
std::swap(tmp,buff);
delete tmp;
}
return *this;
}
Еще лучшим решением является использование копии и замена idium:
CString& operator = (CString rhs) // Note pass by value to get auto copy.
{ // Most compilers will then do NRVO
this->swap(rhs);
// Simply swap the tmp rhs with this.
// Note that tmp was created with copy constructor.
// When rhs goes out of scope it will delete the object.
}
void swap(CString& rhs)
{
std::swap(len, rhs.len);
std::swap(buff, rhs.buff);
}
Теперь давайте разберемся с вашим оператором +
CString operator + (const CString& rhs) const
{
// You could optimize this by providing a private constructor
// that takes two char pointers so that allocation is only done
// once.
CString result(*this);
return result += rhs;
}
CString operator += (const CString& rhs)
{
size_t lenght= len+rhs.len+1;
// Char are easy. No chance of failure.
// But if this was a type with a copy constructor or any other complex
// processing involved in the copy then I would make tmp a smart pointer
// to make sure that it's memory was not leaked if there was an exception.
char* tmp = new char[lenght];
strcpy_s(tmp, lenght, buff);
strcat_s(tmp, lenght, rhs.buff);
std::swap(len, length);
std::swap(buff, tmp);
delete tmp;
}