Carlo said:
Hello,
traditionally, in C++, dynamically allocated memory has been
managed explicitly by calling "delete" in the application code.
I think this is a misuderstanding. If by "traditionally" you mean C++ code
written until about 1998 maybe so, but the current C++ standard along with
auto_ptr<> and various third-party shared_ptr/counted_ptr imlementations
exist since at least 10 years now. Not to mention C++0x is just around the
corner and it will change the way we think C++, so we really need to drop
tradition and use best solutions as offered by a modern C++ implementation.
You probably didn't ment that by traditionally, in that case I'm stating the
above for those that do mean it that way
Now, in addition to the standard library strings, containers, and
auto_ptrs, gurus suggest that may be better to use a reference-counted
smart pointer, or a garbage-collector.
I'm no guru but I'll state my oppinion based on my experience.
There are many "problems" with using blind pointers. All IMO derive from the
fact that a pointer is just too semantically rich. Take for example a C
complex program, you will see pointers used as such:
- 90% of cases used as a reference (that is used to pass by reference
arguments and they actually cannot logically be NULL inside the function
receiving it but no explicit syntax states that)
- 9% of times used to signal optionality of value (that is, if the value
exists then it's != 0 or if it doesn't it is 0)
- 1% used for pointer arithmetic and other advanced pointer semantics
Ok, those numbers are obviously exagerated but you get the idea. Using a
pointer is like designing a class type that has all the program logic in it
(a monster with a huge number of memer functins and data with no invariants
to keep). Good design dictates that classes should be made as much
specialized as you can, to do one simple thing and do it well (and maintain
the invariants of that).
Same with pointers, you need to pass by references something around use a
reference (for which you know it can't be NULL since in order to initialize
a reference to something NULL you would have had to dereference NULL which
is UB anyway). You need to signal optional value, use something like
boost:
ptional (many even very experienced C++ programmers still prefer
pointers for that). You need a pointer to own some memory and delete the
memory on end scope of the pointer, use an scoped_ptr (maybe an auto_ptr if
you prefer only std code or if you need the auto_ptr move semantics). And
so on.
All this specialization helps your program by moving into the C++ compile
time type system alot of your program logic (the fact that there is no
operator++ on auto_ptr<> means you can't do pointer arithmetic by mistake
on auto_ptr<> values, signaled by a compile time error) thus resulting into
a less error prone program not to mention easier to read by a reader that
knows what all those "alternative pointer types" do (since she won't have
to see how do you use a pointer, she sees an auto_ptr and knows already
some facts about your usage of it).
But in which cases it is better to use one technique and in which cases
another? IOW, which is the design criterion?
I think in general you should almost never use pointers. First go through
these alternative solutions and see which best fits your needs: C++
references, boost:
ptional (optional is interesting also because you can
use it to build a sort of pointer value that can be either NULL or point
validly if for example you make an boost:
ptional<T&>), auto_ptr /
scoped_ptr / shared_ptr / weak_ptr.
About gc I can't say much since I haven't used it in C++ (only in Java as it
was forced in). Since for my kind of development I deal with alot of
resources for which RAII and scoped objects map very well I have no need of
gc.
And if, after having completed a working system, a technique would
result more performing than another, or better for other reasons, is it
advisable to change the memory management strategy of that working system?
I think there are some situations in which a gc should perform better than
probably all those solutions above, maybe someone more experienced with
using gc's can provide an example (because all examples I come with right
now I also find them a solution C++ by doing a custom allocator).