To the OP: your comments indicate some confusion as to how C++ works. There are some important things you need to understand a few things:
- Dealing with pointers is inherently dangerous. Incorrectly using pointers may introduce Undefined Behaviour, which not only is an error in your program, but it's an error that may well not be detected by the compiler, not even giving you a wanring, and at runtime there is no telling how your program will behave: it may crash right there and then, but it may also continue running, but cause another part of the program, which is 100% correct, to either crash or just behave differently than it should. There is no automatic way to detect these situations with certainty, so good design is paramount.
- A pointer to an object is a different thing than the object it points to. As a side note, if you come from a language like Java, in C++ the term 'object' also refers to variables of built-in types, like int, double etc.
- A pointer to type X is a value type, that may or may not point to an object to type X. Apart from having a special value of 0 (NULL), it may simply point to a random place in memory which does not contain an object of type X. Such a pointer is called a 'dangling pointer'.
- The act of accessing an object pointed to by a pointer is called dereferencing the pointer. Dereferencing a dangling pointer or a null pointer is Undefined Behaviour. Furthermore, while it is possible for your program to check if a pointer is null, it's not possible to check if it's a dangling pointer. It is the programmer's responsibility to structure the program in such a way, that dangling pointers are never dereferenced.
- In one of your questions, you ask about creating a pointer. Creating a pointer definitely does not mean you have to call
delete
on it, because creating a pointer is different to creating an object to which the pointer would point. Consider the following piece of code, which creates a pointer: SomeType* pointer;
While this does in fact create a pointer, it is a dangling pointer, and if you attempt to call delete
on it, you will get Undefined Behaviour.
- The question of memory allocation is not to do with pointers as such, as with objects, for which memory is allocated or de-allocated. Typically, you do not allocate or de-allocate memory explicitly in C++, but instead create and destroy objects, and memory is allocated for them implicitly.
- The
new
operator does not create pointers, it creates dynamic objects, allocating the required memory for them, and returns a pointer to the newly created object. Dynamically allocated objects' lifetime will persist and they will keep occupying the memory that they occupy until you call delete
on the pointer, which destroys the dynamic object (not the pointer itself) and deallocates the memory it occupies. If you repeatedly allocate dynamic object and fail to delete them after you stop using them, your memory gets filled up with unused objects: this is called a memory leak.
- Pointers may point not just to dynamically allocated objects, they may point to other kinds of objects as well: local variables, structure or class members, array elements. Just as there is no way of telling if a pointer is dangling, there is also no way to tell if it points to a dynamic object. Attempting to call
delete
on a valid pointer that points to anything else than a dynamic object results in Undefined Behaviour.
- There may be many pointers pointing at the same object. If the object is destroyed, either by calling delete in the case of dynamic object, or in any other way, all pointers, that pointed to it, become dangling pointers.
- Pointer variables or class/struct members are objects in their own right, but their lifetimes are not tied to whatever objects they point to: just as no pointed-to object is created when you create a pointer, the object pointed to (if any) is not destroyed when the pointer is destroyed. This makes it possible to safely destroy multiple pointers pointing to the same object, or to destroy dangling pointers.
An additional concern when creating dynamic objects with new
and destroying them with delete
is exception safety: in many cases code that looks at first like it would delete
everything created with a new
might not do so, because an exception is thrown. For example:
{
SomeType *p = new Sometype
// some code
delete p;
}
In this case if 'some code' throws an exception, delete p
is never reached and a memory leak occurs. Silimlarly:
class A {
SomeType *p;
// more members
public:
A(): p(new SomeType()) /* more intializers */ { /* body */ }
~A() { delete p; }
};
In this case, if A constructor throws an exception after p is initialized, the destructor will never get called and you will have a memory leak.
Given these pitfalls, several design techniques exist to manage these problems.
Don't use pointers and/or dynamic objects unless necessary: In many cases, you can get by without dynamically allocating objects. In the case of local variables, where the variable's lifetime is limited to function scope you can just define the object as an automatic variable, so instead of:
{
SomeType* p = new SomeType();
// do something with *p
delete p;
}
you do:
{
SomeType var;
// do something with var
} // var destroyed here
In the second case, var will be destroyed and it's memory will be deallocated when it goes out of scope - even if an exception is thrown.
Similarly, for objects containing other objects, you can include the object directly as a member, so instead of:
class Containing {
Contained* member;
};
you can do
class Containing {
Contained member;
};
In this case the memory for contained member is inside the containing object and will be deallocated when the containing object is deallocated, additionally, if the contained member was initialized during the containing object's construction, but the containing constructor then threw an exception, it is guaranteed that the contained member will be properly destroyed, including its own destructor being called, even though the containing object's destructor isn't called.
As there are no pointers involved in this, you don't have to worry about dangling or null pointers, and you get the additional advantage of reduced time and memory overhead.
The disadvantages are: no recursion in data structures (so for lists, trees etc. you need to use pointers), no polymorphism (a pointer to SomeType may point to an object of SomeType or a type derived from SomeType, an automatic variable of type SomeType can only be of that specific type), and no delayed initialization (this is especially important for class data memebers, whose constructor parameters must always be given in the containing class initializer list, while a pointer can be (re)assigned at any time.
Where dynamic objects must be used, you need to rely on design to make sure your dynamic objects are deleted. The typical pattern for this is the concept of an 'owner' - being an entity responsible for deleting dynamic objects. In this concept, every dynamic object has one and only one 'owner', which is a currently executing function or another object that holds a pointer to the owned object. Thus there may be many pointers to one object, but only one is the owning pointer. This is applicable in many cases: a function that creates a dynamic object that lives only as long as the function runs will own that object, meaning there must be code in the function itself that does the delete. In any type of hierarchical data structures (like trees) a 'parent' object can own its 'children'. Some dynamic data structures, like linked lists, can be wrapped in objects that own their dynamic nodes and are responsible for both creating and deleting them, in this example when an element is added to or removed from the list.
In more complex cases ownership can be transferred between various pieces of code. For example a collection of dynamic objects may assume ownership of objects created outside of it when they are added with an insert
method, and relinquish that ownership with an extract
method with get-and-remove semantics (pop, dequeue...). A function which creates dynamic objects and return pointers to them may pass ownership to the caller, and so on.
With objects maintaining ownership of dynamic objects, you will always want to ensure they are deleted in the destructor, because if it doesn't happen, when the owner gets destroyed, the object owned will remain without an owner, meaning it will never be deleted, effectively becoming a memory leak. The owner can also delete the owned object before it is destroyed itself, which is useful in many circumstances. One caveat though: make sure that the owner is not left with a dangling pointer that it would consider to be pointing at a valid object and try to destroy again. In some cases, you destroy an owned object when you're replacing it with a new one, in which case you don't have this problem, as the owning pointer will now point to the new owned object, which is also valid. In other cases you will want to set the owning pointer to 0, so the object know it does not own anything under that pointer any more (it is also safe to call delete on null pointer).
There are a couple of problems with using manual delete
s in function body or destructors to maintain ownership: first, this ownership is not expressed in code, you have to manage it separately, which is error-prone (as the compiler will and cannot detect any mistakes here). This is further complicated by the exception issue I mentioned above: it is hard, and again, susceptible to errors, to write code so that delete will be called no matter where an exception is thrown.
Smart pointers in boost and standard library, mentioned in other answers, help explicitly express ownership semantics in an exception-safe manner. STL's auto_ptr
and boost scoped_ptr
have strict ownership semantics described above, respectively with and without the ability to transfer ownership. If you are fortunate enough to use the current standard C++, unique_ptr is a replacement for auto_ptr that does away with some of its problems. The shared_ptr
of new standard STL, boost and TR1 has 'shared ownership' semantics, where an object has several 'owners' and is destroyed when the last owner lets go of it. There are advantages and caveats to using each of these smart pointer types, which you should read up on before using them.