Suppose that I define some class:
class Pixel {
public:
Pixel(){ x=0; y=0;};
int x;
int y;
}
Then write some code using it. Why would I do the following?
Pixel p;
p.x = 2;
p.y = 5;
Coming from a Java world I always write:
Pixel* p = new Pixel();
p->x = 2;
p->y = 5;
They basically do the same thing, right? One is on the stack while the other is on the heap, so I'll have to delete it later on. Is there any fundamental difference between the two? Why should I prefer one over the other?
Looking at the question from a different angle...
In C++ you can reference objects using pointers (
Foo *
) and references (Foo &
). Wherever possible, I use a reference instead of a pointer. For instance, when passing by reference to a function/method, using references allows the code to (hopefully) make the following assumptions:delete
the object. It's like saying, "Here, use this data but give it back when you're done".The issue isn't pointers per se (aside from introducing
NULL
pointers), but doing memory management by hand.The funny part, of course, is that every Java tutorial I've seen has mentioned the garbage collector is such cool hotness because you don't have to remember to call
delete
, when in practice C++ only requiresdelete
when you callnew
(anddelete[]
when you callnew[]
).That confused me a lot when I was a new C++ programmer (and it was my first language). There are a lot of very bad C++ tutorials that generally seem to fall into one of two categories: "C / C++" tutorials, which really means it's a C tutorial (possibly with classes), and C++ tutorials that think C++ is Java with delete.
I think it took me about 1 - 1.5 years (at least) to type "new" anywhere in my code. I used STL containers like vector frequently, which took care of that for me.
I think a lot of answers seem to either ignore or just avoid directly saying how to avoid this. You generally don't need to allocate with new in the constructor and clean up with delete in the destructor. Instead, you can just directly stick the object itself in the class (rather than a pointer to it) and initialize the object itself in the constructor. Then the default constructor does everything you need in most cases.
For almost any situation where this won't work (for instance, if you risk running out of stack space), you should probably be using one of the standard containers anyway: std::string, std::vector, and std::map are the three that I use most often, but std::deque and std::list are also quite common. The others (things like std::set and the non-standard rope) aren't used as much but behave similarly. They all allocate from the free store (C++ parlance for "the heap" in some other languages), see: C++ STL question: allocators
Yes, at first that makes sense, coming from a Java or C# background. It doesn't seem like a big deal to have to remember to free the memory you allocated. But then when you get your first memory leak, you'll be scratching your head, because you SWORE you freed everything. Then the second time it happens and the third you'll get even more frustrated. Finally after six months of headaches due to memory issues you'll start to get tired of it and that stack-allocated memory will start to look more and more attractive. How nice and clean -- just put it on the stack and forget about it. Pretty soon you'll be using the stack any time you can get away with it.
But -- there's no substitute for that experience. My advice? Try it your way, for now. You'll see.
Logically they do the same thing -- except for cleanup. Just the example code you've written has a memory leak in the pointer case because that memory is not released.
Coming from a Java background, you may not be completely prepared for how much of C++ revolves around keeping track of what has been allocated and who is responsible for freeing it.
By using stack variables when appropriate, you don't have to worry about freeing that variable, it goes away with the stack frame.
Obviously, if you're super careful, you can always allocate on the heap and free manually, but part of good software engineering is to build things in such a way that they can't break, rather than trusting your super-human programmer-fu to never make a mistake.
I prefer to use the first method whenever I get the chance because: