14 Replies - 3477 Views - Last Post: 10 March 2009 - 12:10 PM Rate Topic: -----

#1 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Operator Overloading and Memory Management

Posted 05 March 2009 - 02:39 PM

Maybe not directly on operator overloading... but associated with it - kind of... it's more about memory management. lemme 'splain!

I'm going back through my SAMS teach yourself C++ in 24 hours as a refresher (as it's been a little while) so i can refresh on that while learning windows programming...

In this book, which i've been tearing through, there's a chapter on operator overloading. In overloading the assignment operator, they give an example, and say that since some members are allocated on the heap, then this memory must be deleted in order to avoid a memory leak. I don't understand why that's necessary. here's the class code:
class CAT
{
public:
	CAT();	//default constructor
	// copy constructor and destructor elided!
	int GetAge() const { return *itsAge; }
	int GetWeight() const { return *itsWeight;}
	void SetAge(int age) { *itsAge = age; }
	CAT operator=(const CAT &);

private:
	int *itsAge;
	int *itsWeight;
};

CAT::CAT()
{
	itsAge = new int;
	itsWeight = new int;
	*itsAge = 5;
	*itsWeight = 9;
}

CAT CAT::operator =(const CAT & rhs)
{
	if (this == &rhs)
		return *this;
	delete itsAge;
	delete itsWeight;
	itsAge = new int;
	itsWeight = new int;
	*itsAge = rhs.GetAge();
	*itsWeight = rhs.GetWeight();
	return *this;
}

So i understand the gist of how this works. What i don't understand is why the deletes are called, where the memory leak comes in, and why not just return a reference CAT. It doesn't go out of scope, so why not a reference? And why delete the memory, instead of just assigning *itsAge = rhs.GetAge() and *itsWeight = rhs.GetWeight()??? Seems unnecessary.

Here's what they say:

Quote

There is an added wrinkle with the assignment operator, however. The object catTwo already exists and already has memory allocated. That memory must be deleted if there is to be no memory leak. So the first thing you must do when implementing the assignment operator is delete the memory assigned to its pointers.


Another question i have is in operator overloading with the increment operator. i understand the overloading of the prefix operator... but when they go into the postfix increment operator, they say the compiler would have a problem determining the difference between overloading the prefix increment operator with the postfix. Fair enough. It says it differentiates by convention with an integer variable supplied as a parameter to the operator declaration. The parameter's value is ignored, it's just a signal that this is the postfix operator. Yet in the implementation, the postfix operator is called in the normal fashion... ie object++; the overlaoding is given as
const Counter operator++ (int);
yet no int is sent in calling it. I guess my question is - is this just some flag for the compiler? Or is there some hidden int actually sent with the ++ operator (i would guess the int would be 1, :) ) - but then if that's the case, where is the differentiation with prefix and postfix increment - wouldn't they BOTH be sent an int of 1??? Should i just accept this and move on??? :)

Is This A Good Question/Topic? 0
  • +

Replies To: Operator Overloading and Memory Management

#2 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3106
  • View blog
  • Posts: 19,145
  • Joined: 14-September 07

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 03:01 PM

Consider the pointer to be holding a map of how to access those values, if you reassign it before deleting what it currently points at you then have no method of accessing the previous memory and it has "leaked". As for this example--IT IS TERRIBLE-- There is no need to allocate those values on the heap; its a solo integer.

As for the increment overload, did they define the function somewhere? You just have the declaration there.
Was This Post Helpful? 0
  • +
  • -

#3 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 04:06 PM

Thanks for the prompt reply!

but in the example, i'm not reassigning the pointer, but rather assigning a new value to an existing pointer... yeah, it's a very ridiculous example... most of the examples in the book are pretty silly :) Anyway, i still don't see how a memory leak would be created by assigning a new value... it's still pointing to the same address in memory, isn't it? Is there a way i can see this in the debugger? Or is this example just not indicative of a memory leak??

Also, is there any reason for this to pass back the object rather than pass back a reference?? It would still be in scope, right?

As for the increment overload, yes, here is the function definition, and how it's used in main... i've omitted any other code surrounding it...
const Counter Counter::operator++(int)
{
Counter temp(*this);
++itsVal; // this is a private member integer variable in the class Counter
return temp;
}

It says it has to return by value, because the temp Counter object would go out of scope... it has to return the state of the object prior to the increment... so i get all that...
here's how they have the implementation:
int main()
{
Counter i;
Counter a;
a=i++;
}
what i'm wondering about that is how i++ tells the compiler which function to use... how it knows to choose the overloaded operator with the (int) as an argument, when the implementation doesn't have an int shown... is there a hidden integer of 1 sent to the function when ++ is implemented, and if so, wouldn't that be sent with both the prefix AND the postfix incrementers? ++i AND i++?

Thanks again!
Was This Post Helpful? 0
  • +
  • -

#4 bsaunders  Icon User is offline

  • D.I.C Addict

Reputation: 44
  • View blog
  • Posts: 571
  • Joined: 18-January 09

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 04:14 PM

I don't understand your question about pointer reassignment. Are you asking why you need to use delete?

This post has been edited by bsaunders: 05 March 2009 - 04:23 PM

Was This Post Helpful? 0
  • +
  • -

#5 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3106
  • View blog
  • Posts: 19,145
  • Joined: 14-September 07

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 04:21 PM

Quote

but in the example, i'm not reassigning the pointer, but rather assigning a new value to an existing pointer...


That is reassigning. The pointer's value changed, you deleted its original value, allocate more memory, and then assign it.
Was This Post Helpful? 0
  • +
  • -

#6 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 05:48 PM

wouldn't this be a better way to do it... can you show me how this would lead to a memory leak??? Sorry if i'm not getting it....
CAT& CAT::operator =(const CAT & rhs)
{
	if (this == &rhs)
		return *this;
	//   delete itsAge;
	//   delete itsWeight;
	//   itsAge = new int;
	//   itsWeight = new int;
	*itsAge = rhs.GetAge();
	*itsWeight = rhs.GetWeight();
	return *this;
}
in this way, first, i've changed it to return a reference. Is there any plausible reason not to do this? I've eliminated the deletes on the pointers itsAge and itsWeight, and the creation of new memory for them.

i am not seeing how this would result in a memory leak. I'm never reassigning itsAge or itsWeight to different areas of memory... i'm changing the values they point to by dereferencing them.... if rhs.GetAge() returns 7, then i'd be writing *itsAge = 7; which merely changes the value, not the area of memory... is the book wrong, or am i missing something????
Was This Post Helpful? 0
  • +
  • -

#7 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3106
  • View blog
  • Posts: 19,145
  • Joined: 14-September 07

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 05:54 PM

Should be fine, unless I'm missing something in the bigger scope of the program. Make sure the Cat's destructor frees up any remaining memory.

edit: Does the book say that will cause a memory leak? Book is probably wrong.

This post has been edited by KYA: 05 March 2009 - 05:55 PM

Was This Post Helpful? 0
  • +
  • -

#8 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 08:46 PM

The book insinuates that if the function returns a copy, and doesn't delete and reassign the pointers, it will create a memory leak. Here's a more complete quote.

Quote

In Hour 13, "Advanced Functions" I discussed the difference between a shallow, or member-wise copy and a deep copy. A shallow copy just copies the members, and both objects end up pointing to the same area on the free store. A deep copy allocates the necessary memory. You saw an illustration of that in Figure 13.1; refer back to that figure if you need to refresh your memory.

You see the same issue here with assignment as you did with the copy constructor. There is an added wrinkle with the assignment operator, however. The object catTwo already exists and already has memory allocated. That memory must be deleted if there is to be no memory leak.

They're saying that were the function to read like this:
CAT CAT::operator=(const CAT & rhs)
{
*itsAge = rhs.GetAge();
*itsWeight = rhs.GetWeight();
return *this;
}

That this call would create a memory leak.
CAT catOne(5,7);
CAT catTwo(3,4);

catTwo = catOne;
Is that true? Is it because it's returning by value, and thus when returning, a shallow copy gets made that then goes out of scope or something? Sorry if i'm being obtuse!!!

This post has been edited by cyphen: 05 March 2009 - 08:48 PM

Was This Post Helpful? 0
  • +
  • -

#9 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 09:02 PM

wait. that can't be. because either way it's returning by value...

so yeah - the book must be wrong, right???
Was This Post Helpful? 0
  • +
  • -

#10 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 05 March 2009 - 09:23 PM

wait wait wait... maybe not!! since *this is being passed back by value, it would do a shallow copy through the default copy constructor, right? And the new memory addresses would be applied in that shallow copy, rather than the old one... the copy doesn't go out of scope, because no real object is created in the interim of a default copy constructor, right? So they're saying that if i used the above code... without the deletes, assigned catTwo=catOne, that the member variables would point to the same memory?

I'm spinning myself into confusion here!

I guess, is there a way you could explain what goes on with the above function calls, and memory? I thought i understood it, but maybe i don't. And now i'm not even sure about what's happening in memory when *this gets passed out of that function by value! Does it call the copy constructor? What happens! ACK !!!!
Was This Post Helpful? 0
  • +
  • -

#11 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 06 March 2009 - 09:20 AM

okay, after reflecting on it, and then doing some debugging tests, i think i've got it figured out. The book is indeed wrong. The only way that scenario could result in a memory leak is if there is no operator overload. If it merely calls the default copy constructor, then yes, catTwo's member variables would be pointing at the same memory as catOne's.

The copy constructor is called when returning by value, but it doesn't matter because it's returning itself. So it's doing a shallow copy of it's own variables. Thus no memory leak.

It makes a lot more sense to me to return by reference, since there's no need for the copy constructor to be called. In fact, it makes more sense to me for the function not to return a value at all, but when i tried that, i got an error on the line
*itsAge = rhs.GetAge();

the compiler complained that a void function was returning a value. Seems weird to me that it can't call a function returning a value... the function itself wasn't returning a value...

Anyway... i guess that's a mystery for another day!

I still am interested in the postfix incrementor... why the implementation of an object i++ doesn't match the overloaded operator function signature of taking an int... does only the postfix version of the ++ operator send an invisible int as an argument???

Anyway, thanks for the help!
Was This Post Helpful? 0
  • +
  • -

#12 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3106
  • View blog
  • Posts: 19,145
  • Joined: 14-September 07

Re: Operator Overloading and Memory Management

Posted 06 March 2009 - 09:32 AM

getAge() has to return a value. Is that what you're referring to?

As for the increment, i need to run a few things to verify, but I'm almost positive that you have to have a var name for the int. I'll brb with a code example.


edit: I wrote about prefix vs post fix in this tutorial

The idea is basically:

Instead of incrementing and then fetching, we must now fetch and then increment:

class Counter
{
public:
	Counter();
	~Counter();
	int GetItsVal()const {return itsVal;}
	void SetItsVal(int x) {itsVal = x;}
	void Increment() {++itsVal;}
	const Counter& operator++(); //prefix
	const Counter operator++(int); //postfix

private:
	int itsVal;
};

Counter::Counter():
itsVal(0)
{}

Counter::~Counter()
{}

const Counter& Counter::operator ++()
{
	++itsVal; //increment and then return
	return *this;
}

const Counter Counter::operator ++(int) //fetch then increment--scope issues
{
	Counter temp(*this);
	++itsVal;
	return temp;
}

int main()
{
	Counter i; //declares a counter object'i'
	cout << "The value of i is " << i.GetItsVal() << endl;
	i.Increment();
	cout << "The value of i is " << i.GetItsVal() << endl;
	++i;
	cout << "The value of i is " << i.GetItsVal() << endl;
	Counter a = ++i;
	cout << "The value of a: " << a.GetItsVal();
	cout << " and i: " << i.GetItsVal() << endl;
	a = i++;
	cout << "The value of a: " << a.GetItsVal();
	cout << " and i: " << i.GetItsVal() << endl;
	return 0;
}


This post has been edited by KYA: 06 March 2009 - 09:37 AM

Was This Post Helpful? 0
  • +
  • -

#13 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 06 March 2009 - 02:34 PM

Actually, this is what i was referring to... i thought... well, if the function is changing the members it needs to change already, why return any value at all from the operator=() overload?? So i tried this:
class CAT
{
public:
	CAT();	//default constructor
	// copy constructor and destructor elided!
	CAT(const CAT&) {cout << "COPY CONSTRUCTOR CALLED!\n";}
	int GetAge() const { cout << "itsAge address: " << itsAge << endl; return *itsAge; }
	int GetWeight() const { cout << "itsWeight address: " << itsWeight << endl; return *itsWeight;}
	void SetAge(int age) { *itsAge = age; }
	void operator=(const CAT &);

private:
	int *itsAge;
	int *itsWeight;
};
void CAT::operator =(const CAT & rhs)
{
	*itsAge = rhs.GetAge();
	*itsWeight = rhs.GetWeight();
}


I'll be darned! I swear that when i tried that the first time, it complained that rhs.GetAge() was returning a value in a void function... i must have accidentally set that function to void or something - i just ran it now and it worked just fine...

That just points to more silliness in the example... there doesn't appear to be any need whatsoever to return anything from the assignment overload function. Not by value, not by reference - not at all... Sometimes these examples can mislead you on good coding practices!!!

On the incrementor overloading, that's almost exactly the example in the book i'm reading...

what i'm wondering is how ++i, and i++ are passed to the functions so that they know which overloaded operator to call? The postfix is looking for an int to be passed, but i++ doesn't pass an int, does it? Am i looking to far into this? Should i just accept that the compiler uses the (int) to flag ++ as a postfix overload, and that the context of the code (ie i++; ) sends it to the right function since it's used as a postfix?

I guess i was just curious if there's some hidden mechanism at work which actually passes an int value that gets discarded when you do i++; versus ++i;

This post has been edited by cyphen: 06 March 2009 - 02:36 PM

Was This Post Helpful? 0
  • +
  • -

#14 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3106
  • View blog
  • Posts: 19,145
  • Joined: 14-September 07

Re: Operator Overloading and Memory Management

Posted 06 March 2009 - 04:36 PM

Yes. There's a secret rule that when overloading postfix operator it must pass a parameter of int.

"the second formal parameter to postfix 'operator ++' must be 'int'"


edit: It's not secret. I thought that would make it sound funny, but it comes off as way too sarcastic. :)

This post has been edited by KYA: 06 March 2009 - 04:37 PM

Was This Post Helpful? 0
  • +
  • -

#15 cyphen  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 11
  • Joined: 05-March 09

Re: Operator Overloading and Memory Management

Posted 10 March 2009 - 12:10 PM

lol... it's all good! :) Thanks for the help!
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1