Compound assignment and add operator overloading

2019-08-18 21:44发布

问题:

I need help with both of my operator overloading functions presented below. I'm unsure of how I can implement this without actually using the assignment in the function definitions.

Code for operator + in my .cpp file:

MyString& MyString::operator +(const MyString& rhs)
{
  delete [] String;
  String = new char[rhs.Size];
  Size = rhs.Size;
  // needs to be a loop for cascading + 
  // so that String1=String2+String3+String4 will work
  for(int i = 0; i < rhs.Size+1 ; i++)
  {
    //  String[i] + rhs.String[i]; ???
  }
  return *this;
}

Code for += operator in .cpp file:

MyString& MyString::operator+=(const MyString& rhs)
{
  delete [] String;
  String = new char[rhs.Size];
  String = String + rhs.String;
  return *this;
}

Call from main.cpp:

 String1 = String2 + String3 + String4;
 String1.Print ();

 String2 += String3;
 String2.Print ();

I know my .cpp file codes are wrong, some insight would be great!

回答1:

First, usually you return a new object from operator+, because the expectation is that calling + on an object does not change the object itself.

MyString MyString::operator+ (const MyString& rhs)  
{  
  // ...

  return MyString(...);  

} 

Note the missing reference (&) from the return type: you are returning the new object by-copy, not by-reference.

Second, if you delete the String at the beginning, you will not be able to copy its contents. Consider this for operator+:

char* tmp = new char[Size + rhs.Size + 1]; // +1 for the terminating '\0'      
for(int i = 0; i < Size ; i++)      
{
  // copy the contents of current object buffer, char-by-char
  tmp[i] = String[i]; 
}
for(int i = 0; i < rhs.Size+1; i++) // +1 to copy the terminating '\0' as well
{      
  // copy the contents of other object buffer, char-by-char
  tmp[i+Size] = rhs.String[i]; 
}
MyString result;
delete[] result.String;
result.String = tmp;
result.Size = Size+rhs.Size;      

return result;

operator+= is a bit more tricky, because you need to manipulate the current object's buffer:

char* tmp = new char[Size + rhs.Size + 1]; // +1 for the terminating '\0'      
for(int i = 0; i < Size ; i++)      
{      
  tmp[i] = String[i]; 
}
for(int i = 0; i < rhs.Size+1; i++) // +1 to copy the terminating '\0' as well
{      
  tmp[i+Size] = rhs.String[i]; 
}
delete[] String;
String = tmp;
Size += rhs.Size;      

return *this;

Update: I assume you also call delete[] in the class destructor -- which you should. It is also not hard to imagine, you will want to perform assingment from one MyString object to another. This will lead to cosider the rule of three: If you need any one of desgtructor, copy-constructor or assignment operator, you most likely need all three.



回答2:

The idiomatic way is to implement the functionality in operator+= and then use that to implement operator+. Assume for starters than your operator+= is correctly implemented, then operator+ is trivially implementable as a free function:

MyString operator+( MyString lhs, MyString const & rhs ) {
   lhs += rhs;
   return lhs;
}

Note: The first argument is passed by value, so that it is a copy of the original that we can modify through operator+=. There are some other tips you might find useful here.

Now back to implementing operator+=, the first thing that you should understand is what the operations that you need to perform are: you need to allocate a longer buffer, copy from the old buffer, append the rhs string, swap the old buffer and the new (which contains the result) and free the old buffer. The order of the operations is important, if you release the old contents before copying (as you are doing) then you can no longer copy from that.

// Rough approach
MyString& operator+=( MyString const & rhs ) {
   char * new_buffer = new char[ Size + rhs.size + 1];       // [1]
   std::copy_n( String, Size, new_buffer );
   std::copy_n( rhs.String, rhs.Size + 1, new_buffer+Size ); // [2]
   swap(String, new_buffer);                                 // [3]
   Size = Size + rhs.Size;
   delete [] new_buffer;
   return *this;
}

[1]: Allocate the new buffer and copy to it. Note that in this particular case the code is correct because none of the rest of the instructions in the function can throw an exception. If that was not the case, the new buffer should be managed through RAII to ensure at least the minimal exception safety.

[2]: Assumes that as an invariant of the type MyString there is always a null terminator. The Size+1 in the count argument will copy all elements and the null terminator.

[3]: At this point all the operations have been performed, we can exchange the old and new buffers, update the size and release new_buffer (that actually refers to the old buffer)