Click here to Skip to main content
15,884,628 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi, imagine I have some String class, e.g., String.
I also have following code:
C++
String x,y,z;
z=x+y;

Now, if I implement the + operator of my String class in
the following way:
C#
String operator + (const String& other) const
       {

             String result;
             //..
             // copy existing string to result
             // append other to it
             return result;
            
       }

My initial code will have problem - because z will point to a object (returned by x+y)
which was destroyed.

How to avoid this situation, assuming I don't want to write an assignment operator which does deep copying. I could have the + operator in the following way:
C#
String operator + (const String& other) const
       {

             String *result=new String();
             //..
             // copy existing string to result
             // append other to it
             return *result;
            
       }
but then how do I delete the memory I allocated inside operator +?

Thanks.
Posted
Updated 14-Mar-13 1:01am
v2

Your original version is actually the correct one. You create a temporary String object inside your operator+ function and return it by value to the caller. In the function return code, the value will be copied to another temporary string on the stack and then via the assignment operation that value will be copied to your variable z. (Don't worry, the compiler can optimize a lot of this copying away for you.)

What's probably not correct is the way in which you implemented the copy constructor and assignment operators. If you are not receiving the correct value in z, then look into these functions; you may also show us the code of them by using the green "Improve Question" button, so we can give you a little help with that.

Now to your alternative code:
// DON'T DO THAT!!!
String operator + (const String& other) const
       {

             String *result=new String();
             //..
             // copy existing string to result
             // append other to it
             return *result;

       }

Here is what happens. You allocate a new String on the heap and store its pointer in result. So far so good. On return the value of this object is put on the stack and returned the caller, i.e. it is copied because you are returning a String value and not a String*. Your pointer to the object on the heap is lost forever and hence you have created a memory leak. That's why this is no good idea.

There is a second reason: Allocating an object on the heap is much more expensive in terms of time than an allocation on the stack, because it requires thread synchronization in multi-threaded applications. So avoid that in low-level functions whenever possible.
 
Share this answer
 
Comments
[no name] 14-Mar-13 7:51am    
My copy constructor just does a shallow copy, e.g., if I have
class String
{
StringBuff *m_buff;
}
The copy constructor will just to a shallow copy, make m_buff point to the m_buff address of the object which we want to copy. That is why my above code is problematic, the m_buff starts to point to a variable which was destroyed. I don't want to make a deep copy in my copy constructor yet. That is why I asked this question. ps. this is how my = operator looks like:
String& operator = (const char* source)
{
(*this).Assign(source);

return *this;
}
nv3 14-Mar-13 8:26am    
You can continue doing it in this economical way, but then you have to implement reference counting on that buffer pointer. That is a little bit tricky, but manageable. The way you have implemented it now will not only cause problems in the given scenario, but also in various other examples.
[no name] 14-Mar-13 8:40am    
I have reference counters. The reason I don't want deep copy is because my string is "lazy" , when you copy a string, you don't want to do a deep copy unless someone tries to modify the string (and then, you make a brand new copy of the string and modify that one). So, what is the solution in my situation? Thank you. ps. you could also check the last comment of Mathew Faithfull.
nv3 14-Mar-13 10:30am    
If you do reference counting on the buffer you actually should not have the problem. Here is the scenario: Let's considers what should happen in your original code:

1. you construct String result on the stack and assign to it the concatenation of x and y
2. you execute the "return result" statement, which will:
a) create a temporary String object on the stack by using the copy constructor; that will increase ref count on your buffer
b) destroy the object "result", which will not delete the buffer, because its ref count is 2; instead it just decrements the ref count to 1.
3) assign the temp object to z, which will first increment the ref count to 2 again
4) the temp object is destroyed, which again decrements the ref count to 1 and does not delete the buffer.

So now z is using the buffer that you had originally allocated in String "result". And all is fine.

If you like, then post your ref counting code as amendment to your original question and I will take a look at it.

[no name] 14-Mar-13 11:48am    
H, nav3. Thanks for your feedback, here is my copy constructor:
String(const String& s): data(s.data)
{

// Increase reference count.
++data->ref;
}
data - member variable is some data struct, let's say StringBuff.
I am getting an exception due to x=y+z code, but one clue that we may have
in solving this issue, is that in the debugger, after I cross the x=y+z line,
indeed x's data structure is pointing to the buffer we allocated in + operator,
however ,it's reference count is not initialized and is a negative value and *also* the char * buffer which is located inside the StringBuff class points to a _badpointer_.
I think this is the problem. What can be causing this? Thank you.
You delete the allocated memory in the same way as you always do by calling delete with the address of the allocated memory. In this case something like.

String& z = x + y;
...
delete &z;


This is not how most string classes work because they have the allocated memory assigned to an internal member variable, e.g.

class String
{
...
private:
 unsigned char* m_pStringMemory;
...
};


The internal pointer is always deleted in the destructor of the containing class e.g.
String::~String()
{
  delete m_pStringMemory;
}

so given correct assignment, copy construction and + operator member functions you can then do
String x, y, z;
x = y + z;

and no need to clean up at this external level because everything here is on the stack. The automatic destruction of x when it's stack frame is unwound will call String::~String() and free the memory allocated by the + operator.
 
Share this answer
 
Comments
[no name] 14-Mar-13 7:27am    
Hm... what you suggest is interesting actually in my case also my String class inside has an internal member variable e.g., (the copy constructor just does a shallow copy)
class String
{
...
StringBuff* m_pStringMemory;
...
}
I am also deleting it in destructor. So you suggest if I implement + operator as in my initial post (using new) and do x=y+z - I will not need to care about deleting the memory that was allocated in the + operator?
Matthew Faithfull 14-Mar-13 7:41am    
As long as you manage the memory correctly inside the class, for example delete and reallocate in the assignment operator, reuse the assignment operator to implement the copy constructor etc then yes the destructor will always do the final cleanup for you. Note what nv3 says below about loosing assigned pointer values by returning copies of the item they point to but this should not be an issue if you are keeping the m_pStringMemory internally, only ever assigning allocated memory to that, initialising it to NULL in all your constructors ( preferably in the initializer list ) and always remembering to delete it before assigning a new allocation to it ( delete NULL is always safe after early versions of VC6 ).
[no name] 14-Mar-13 7:33am    
This solution:
String& z = x + y;
...
delete &z;
here would not I have to change the signature of the + operator to:

String& operator + (const String& other) const?
Matthew Faithfull 14-Mar-13 7:43am    
No. A reference can be constructed by assigning an object to it. In fact it can't be constructed otherwise.
[no name] 14-Mar-13 7:55am    
yes but will this make sense? as nv3 said, when operator + returns, it will return a copy of *result - meaning delete &z will delete something else, not the initial String *result = new String() variable which I allocated inside the operator +.
// How to avoid this situation ?

Just make sure, that the String has a copy constructor.
In this case, the result will be copied onto the stack, before its destroying :)
 
Share this answer
 
Comments
nv3 14-Mar-13 11:12am    
Eugen, please see OPs comments above. He tries to avoid a deep copy, which is smart for a good string class. I just have the feeling that the ref counting scheme is not implemented correctly.
Eugen Podsypalnikov 14-Mar-13 11:25am    
OK... Then we will need a memory manager for all such strings, to register and handle their references(inc/dec), sharing and modifing to/of the data :)
nv3 14-Mar-13 11:38am    
Not necessarily. The ref counting could be done in the buffer struct. Matthew has described it nicely in his comment above. That's btw. how MS implemented their CString class.
Eugen Podsypalnikov 14-Mar-13 12:00pm    
OK, in this case the String must handle its ref/unref/clone needs by itself... :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900