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?
The reason is that pointers are used to bodge into C some vital features which are missing from the original language: arrays, strings, & writeable function parameters.
Security: By not allowing pointers, Java effectively provides another level of abstraction to the developer. No pointer support make Java more secure because they point to memory location or used for memory management that loses the security as we use them directly.
Pointers save memory space. Execution time with pointers is faster because data are manipulated with the address, that is, direct access to memory location. Memory is accessed efficiently with the pointers. The pointer assigns and releases the memory as well.
Using pointer in C programming has following disadvantages: If pointers are referenced with incorrect values, then it affects the whole program. Memory leak occurs if dynamically allocated memory is not freed. Segmentation fault can occur due to uninitialized pointer.
Yes, one is on the stack, the other on the heap. There are two important differences:
delete
yourself, instead wrapping it in stack-allocated objects which call delete
internally, typicaly in their destructor. If you attempt to manually keep track of all allocations, and call delete
at the right times, I guarantee you that you'll have at least a memory leak per 100 lines of code.As a small example, consider this code:
class Pixel { public: Pixel(){ x=0; y=0;}; int x; int y; }; void foo() { Pixel* p = new Pixel(); p->x = 2; p->y = 5; bar(); delete p; }
Pretty innocent code, right? We create a pixel, then we call some unrelated function, and then we delete the pixel. Is there a memory leak?
And the answer is "possibly". What happens if bar
throws an exception? delete
never gets called, the pixel is never deleted, and we leak memory. Now consider this:
void foo() { Pixel p; p.x = 2; p.y = 5; bar(); }
This won't leak memory. Of course in this simple case, everything is on the stack, so it gets cleaned up automatically, but even if the Pixel
class had made a dynamic allocation internally, that wouldn't leak either. The Pixel
class would simply be given a destructor that deletes it, and this destructor would be called no matter how we leave the foo
function. Even if we leave it because bar
threw an exception. The following, slightly contrived example shows this:
class Pixel { public: Pixel(){ x=new int(0); y=new int(0);}; int* x; int* y; ~Pixel() { delete x; delete y; } }; void foo() { Pixel p; *p.x = 2; *p.y = 5; bar(); }
The Pixel class now internally allocates some heap memory, but its destructor takes care of cleaning it up, so when using the class, we don't have to worry about it. (I should probably mention that the last example here is simplified a lot, in order to show the general principle. If we were to actually use this class, it contains several possible errors too. If the allocation of y fails, x never gets freed, and if the Pixel gets copied, we end up with both instances trying to delete the same data. So take the final example here with a grain of salt. Real-world code is a bit trickier, but it shows the general idea)
Of course the same technique can be extended to other resources than memory allocations. For example it can be used to guarantee that files or database connections are closed after use, or that synchronization locks for your threading code are released.
They are not the same until you add the delete.
Your example is overly trivial, but the destructor may actually contain code that does some real work. This is referred to as RAII.
So add the delete. Make sure it happens even when exceptions are propagating.
Pixel* p = NULL; // Must do this. Otherwise new may throw and then // you would be attempting to delete an invalid pointer. try { p = new Pixel(); p->x = 2; p->y = 5; // Do Work delete p; } catch(...) { delete p; throw; }
If you had picked something more interesting like a file (which is a resource that needs to be closed). Then do it correctly in Java with pointers you need to do this.
File file; try { file = new File("Plop"); // Do work with file. } finally { try { file.close(); // Make sure the file handle is closed. // Oherwise the resource will be leaked until // eventual Garbage collection. } catch(Exception e) {};// Need the extra try catch to catch and discard // Irrelevant exceptions. // Note it is bad practice to allow exceptions to escape a finally block. // If they do and there is already an exception propagating you loose the // the original exception, which probably has more relevant information // about the problem. }
The same code in C++
std::fstream file("Plop"); // Do work with file. // Destructor automatically closes file and discards irrelevant exceptions.
Though people mention the speed (because of finding/allocating memory on the heap). Personally this is not a deciding factor for me (the allocators are very quick and have been optimized for C++ usage of small objects that are constantly created/destroyed).
The main reason for me is object life time. A locally defined object has a very specific and well defined lifetime and the the destructor is guaranteed to be called at the end (and thus can have specific side effects). A pointer on the other hand controls a resource with a dynamic life span.
The concept of who owns the pointer. It is the responsibility of the owner to delete the object at the appropriate time. This is why you very rarely see raw pointers like that in real programs (as there is no ownership information associated with a raw pointer). Instead pointers are usually wrapped in smart pointers. The smart pointer defines the semantics of who owns the memory and thus who is responsible for cleaning it up.
Examples are:
std::auto_ptr<Pixel> p(new Pixel); // An auto_ptr has move semantics. // When you pass an auto_ptr to a method you are saying here take this. You own it. // Delete it when you are finished. If the receiver takes ownership it usually saves // it in another auto_ptr and the destructor does the actual dirty work of the delete. // If the receiver does not take ownership it is usually deleted. std::tr1::shared_ptr<Pixel> p(new Pixel); // aka boost::shared_ptr // A shared ptr has shared ownership. // This means it can have multiple owners each using the object simultaneously. // As each owner finished with it the shared_ptr decrements the ref count and // when it reaches zero the objects is destroyed. boost::scoped_ptr<Pixel> p(new Pixel); // Makes it act like a normal stack variable. // Ownership is not transferable.
There are others.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With