Okay you have quite a few problems here that I noticed right off the bat:
1) Your + operator is behaving like a += operator. Consider the following code:
1 2 3 4
|
int a = 5, b = 2;
int c = a + b;
cout << a; // you expect this to output 5
|
You expect output to be 5 because you set 'a' to 5. Logically, the + operator should not change a.
Your + operator
changes 'this' and thus if you do something like this:
1 2 3 4
|
Points a, b;
// init a and b here
Points c = a + b; // surprise! this line will change a!
|
2) Your
delete this
bit is fundamentally flawed for a few reasons. It's a very specific design pattern for objects to "commit suicide", one which you're not really doing. wxWidgets does it, but in that context it makes sense
only because the objects that commit suicide own themselves. Here, ownership of your Points does not belong to the Points class, and therefore suicide is ill advised.
Not to mention, even when you suicide, you should
never suicide in the destructor.
Think about it... let's say I do the following:
1 2
|
Lists a = new Lists;
a->Delete();
|
Here's what happens:
- a->Delete() does
delete this
- delete calls the dtor
- the dtor calls Delete()
- Delete() does
delete this
again
- delete calls the dtor again
- the dtor calls Delete() again
- etc
- etc
Nevermind the fact that it's an infinite loop, but you're also
deleting the same buffer multiple times which is terrible.
3) I didn't look closely at your 'temporary' trick, but it sounds like a bad idea. It sounds like it's pseudo-reference counting. Honestly, since you're still struggling with proper encapsulation and memory management, I really recommend you avoid such minor optimization attempts. Get it working first.
Maybe if you're not happy with the performance later you can go back and optimize it. But you're biting off more than you can chew.
Besides... the best way to optimize away expensive copies is to not make unnecessary copies of objects.
(Actually on closer look your 'temporary' trick opens tons of memory leaks by allocating objects with new and never deleting them. Not to mention it gives you tons of duplicated code)
4) You also have some const correctness issues, but that's very minor.
Anyway -- basically you're doing too much. You need to focus on how memory is managed.
Here's some guidelines:
a) Define a rigid model to specify how much memory is allocated and when. In your case this can be very simple. All you really have is that 'first' pointer... so you can say that if 'first' is null, then nothing is allocated... and if 'first' is non-null, then memory is allocated.
b) Minimize the number of functions that allocate/free memory. If you have multiple ways to add new nodes (like with the + operator) try to write one common functions that adds nodes and have your + operator call it. Don't add the nodes directly in every single function. The fewer places you do it, the fewer chances there are to screw up.
c)
Clearly define who owns what and allocate/free memory accordingly. Simply put...
whoever allocates the memory is in charge of freeing it. Logically, Points should own the list of points pointed to by the 'first' pointer. Therefore Points (and no one else) should be responsible for allocating the points. And Points (and no one else) should free those points when it's done with them.
Points does not own instances of itself, and therefore it should not be attempting to delete instances of itself. This is wrong on so many levels.
I don't know how much that helps.
Here's an example of how I would do it (note this isn't perfect as it's not exception safe, but making it exception safe would make it more complicated. One step at a time...):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
class Points
{
public:
// the default ctor. zeros out the pointer and size members
// so it is clear that nothing is allocated
Points()
: first(0), length(0)
{}
// the copy ctor. Copies data from another Points object
Points(const Points& p)
: first(0), length(0) // do this to indicate no memory is allocated (yet)
{
Copy(p); // note: let 'Copy' do all the work
}
// the assignment operator
Points& operator = (const Points& p)
{
Copy(p); // copy the points from p
return *this;
}
/*
Note that both the copy ctor and assignment operator call the same 'Copy' function.
This lets us avoid duplicate code, since they both basically do the same task, just in slightly different ways.
*/
~Points()
{
Clear(); // clear out all existing points (freeing memory)
// that's all you have to do here
}
/*
Notice with the above design, we have a fully functional copy ctor, assignment operator, default ctor, and dtor.
And ZERO memory management code. It's all very simple, right?
Everything is taken care of by Clear and Copy. This is what I meant by my point 'b' above. Keep the memory management
code in all the same place. Minimize the number of functions that mess with that stuff.
*/
void Clear()
{
// step through the nodes and delete all of them
// then reset 'first' and 'length' to zero to indicate that no memory is allocated
// (remember point 'a' above. Stick with your defined terms)
}
void Copy(const Points& p)
{
Clear(); // clear the existing points (if any)
// then copy all the points from p
}
};
|
It's really that simple.
Anyway I'm about out of steam... and this is a pretty length post. If you read it all and still have questions, lemme know.