Why people use "new" & "delete" too much?!!

J

James Kanze

[ ... ]
Pointers are the data structure equivalent of the GOTO. What
end up as gotos (jumps) in assembly language should normally
be hidden, with what's visible being only high level block
structures. Likewise, pointers should typically be hidden,
used to implement high level block structures.

The difference with regards to goto is that the language
supports the necessary high level structures directly. This is
less true with regards to data structures; the library certainly
supports some (e.g. you don't need pointers to implement a list,
because the structure is already there), but certainly not all.
So you end up in the situation you were in in assembler, or,
say, Fortran IV. You use goto (pointers), but in structured
manner.

Note that some languages (e.g. lisp) don't need pointers, since
they do have the high level structures for manipulating them.
At least in the ways that are appropriate in that language.
Programs where there's a lot of (visible) use of new, rather
than most use being hidden inside the implementation of higher
level structures aren't _necessarily_ evil -- but it's
definitely a _strong_ indication of a likely problem.

The new isn't a problem; if the data has dynamic lifetime, you
need a new. The pointers themselves aren't really evil either,
in such cases, provided they're used correctly. At least, at
present, there's not much alternative---I've yet to see any
library or smart pointers which are effective for general
navigation. (Back when I started C++, "relationship management"
was an in thing, and everyone was trying to define classes to
manage relationships. None of the ones I saw really succeeded,
however, and I have the impression that people have pretty much
given up developing a generic solution.)
 
J

Jerry Coffin

(e-mail address removed)>, (e-mail address removed)
says...

[ ... ]
The difference with regards to goto is that the language
supports the necessary high level structures directly. This is
less true with regards to data structures; the library certainly
supports some (e.g. you don't need pointers to implement a list,
because the structure is already there), but certainly not all.
So you end up in the situation you were in in assembler, or,
say, Fortran IV. You use goto (pointers), but in structured
manner.

I mostly agree. I think the ability to create smart pointers (for one
example) gives _more_ capability for ensuring against many of the
problems that can and do arise, however.
Note that some languages (e.g. lisp) don't need pointers, since
they do have the high level structures for manipulating them.
At least in the ways that are appropriate in that language.

Though the syntax is certainly different, dotted expressions in Lisp
make the pointers much more explicit.
The new isn't a problem; if the data has dynamic lifetime, you
need a new. The pointers themselves aren't really evil either,
in such cases, provided they're used correctly.

I'm not saying that the new or the pointer is the problem -- the
_visibility_ is. The new should nearly always be hidden inside of the
implementation of a data structure, preferably the most specialized,
restricted data structure that can do the job. When you see uses of new
all over the program, it's usually (usually, not always) a sign that the
data structure being built and maintained isn't well encapsulated.
 
P

Pascal J. Bourguignon

2-3 machine instructions still isn't "free". However, agree, it is
very very cheap. Maybe even insignificant.

However, unless one is in the special case of short running
program, one shouldn't ignore the cost of running the collector. I
mean at that point, a non-garbage collected language should simply
reply with a proof of speed that consist in a small program that
doesn't allocate any moemory at all.

Let's be a little more forward looking. We've got nowadays multicore
processors, and in a few years, we'll have thousands of core at our
avail. There are garbage collectors who can work constantly in
parallel, in a background thread. In elapsed time, if you allocate
memory in a few instructions, and have the garbage collected in
parallel, the total elapsed time cost is only that of allocating. On
the other hand if you keep a heavy allocation, you cannot run it in
parallel, and your elapsed time will be worse, even if you fork the
free to a background thread.

You know, that's the funny thing: When I write C++, a very rarely feel
that a garbage collector would help me write code faster or result in
more secure and robust code.

Yes, that's because there are other problems with C++ that are
blocking us as well.

STL containers are used extensively.
I rarely use "new", when I do it it's generally inside object and
managed using RAII or ownership is clear anyway.

STL being one of them.

The few cases left
are rare enough that I don't find it a burden nor a security risk.
Even with the said GUI application, to me there's a clear ownership
relationship between a window and a button on that window so lifetime
management is simple.

While your application only has tree-like dependencies between
objects, it's sufficient indeed. But in OO, you often get at one
object deep inside the tree, and would like to move up to some parent.
For example, you may get an event on the Button, and need to know what
window it is in to send it an appropriate message. Therefore you will
need bidirectional relationships, and then lack of a garbage collector
is really a PITA.

However, when I write C. I do find it problematic. The absence of
both garbage collection and RAII makes memory management a constant
pain, slows development and create risks.

Unfortunately, when I look at some other code that is meant to be
compiled with a C++ compiler, things are not always as nice.
Innapropriate habits (maybe imported from C, Java or simply never
learnt better) can introduce poor memory management patterns which
leads to slow development and security and robustness issues.

So a couple of possible solutions:

- Training: teach peoples to write good code. Writing good code
doesn't take longer than writing poor code, it just requires more
skill.
- Garbage collection: has clear advantages in some situations. But is
it a substitute for poor skills?
- Remove flexibility from the C++ language: Is one of the weakness of
C++ its great flexibility? It brings as cost, the capability of
writing poor code which is somewhat limited in a less flexible
language. (witness for example python's attempts to enforce "nice"
formatting by making formatting and whitespaces part of the syntax)

Well, I wouldn't say that C++ is really "flexible" (compared to Lisp),
but indeed, it feels more like you have to finish it yourself before
you can use it, implementing the language features you need to be able
to work in some adequate C++ library.


For example, if don't like new/delete (after all, they give you low
level *pointers*), you will have to implement your own 'dynamic object
reference' embedded into 'smart pointers'.
 
P

Pascal J. Bourguignon

Jerry Coffin said:
(e-mail address removed)>, (e-mail address removed)
says...

Though the syntax is certainly different, dotted expressions in Lisp
make the pointers much more explicit.

The dot in lisp is a syntactic marker to represent CONS cells, not pointers.

(a . b) is a record of two slots containing the symbols a and b.

It's equivalent to something like: std::pair<Object> {a,b}

(cons e1 e2) being equivalent to std::pair<Object>(e1,e2), but with
the pair allocated on the heap.


Lists are implemented as a chain of these CONS cells, of which the
last second slot contains the symbol NIL. A list containing a, b and
c would be stored as:

(a . (b . (c . nil))) == pair<Object>(a,pair<Object>(b,pair<Object>(c,nil)))

it can be printed and read as:

(a b c)

and constructed either with (cons 'a (cons 'b (cons 'c nil))) or
(list 'a 'b 'c).

While there may be pointers at the implementation level, there's no
such notion at the lisp level. Only you can work at the level of
lists, or you can work at the level of the cons cells of which lists
(and other data structures) are made.
 
J

James Kanze

James Kanze <[email protected]> wrote:

[...]
You know, that's the funny thing: When I write C++, a very
rarely feel that a garbage collector would help me write code
faster or result in more secure and robust code.

The security issue is clear: garbage collection reduces the risk
of a dangling pointer actually pointing to some other object
(which can be a security leak, much like buffer overflow), and
more generally, allows reliable checking for dangling pointers.

The win in development time depends largely on what you're
doing; it's true that there are many cases in C++ where it isn't
that relevant. But there are still a few cases where you'll
prefer pointers to dynamically allocated objects to pure value
semantics:

-- when the objects are big enough to make the cost of copying
an issue,
-- when the object is used in an exception, and copying isn't
guaranteed no throw (if an object is to be thrown, you'll
use std::string*, rather than std::string), and
-- when the object must be polymorphic, although otherwise
value semantics apply.

Now it's true that tr1::shared_ptr can be used in the first two,
but that does mean extra effort on your part, albeit minimal.
In the third case, you may have to worry about cycles as well.
And in all cases, it means thinking about it, rather than just
using a pointer and being done with it.

(There are also special cases. I recently had to deal with a
case where I needed a complex data structure which could be
statically initialized. But wasn't always. Because of the
requirement of static initialization, the elements in the
structure couldn't have constructors of destructors, which meant
that in the case of dynamic management, I had to visit the
structure manually to do the deletes. Getting the code
exception safe was a real pain, whereas with garbage collection,
it would have represented 0 effort.)
STL containers are used extensively. I rarely use "new",
when I do it it's generally inside object and managed using
RAII or ownership is clear anyway. The few cases left are
rare enough that I don't find it a burden nor a security risk.
Even with the said GUI application, to me there's a clear
ownership relationship between a window and a button on that
window so lifetime management is simple.

The security risk is still there, as soon as you have objects
with arbitrary lifetimes. (Like buffer overflow, it can only
occur as a result of a programming error. But programming
errors exist, and the fact remains that using a dangling pointer
which points to a new object can result in your code being
compromized.) For the rest, it's certain that you don't need
garbage collection in the way that Java needs it; that it's
impossible to write correct code without it. But there are
enough cases where it does help that one or the other will
inevitably show up in most applications.
However, when I write C. I do find it problematic. The
absence of both garbage collection and RAII makes memory
management a constant pain, slows development and create
risks.

Yes. Without RAII, garbage collection is an absolute necessity.
Unfortunately, when I look at some other code that is meant to
be compiled with a C++ compiler, things are not always as
nice. Innapropriate habits (maybe imported from C, Java or
simply never learnt better) can introduce poor memory
management patterns which leads to slow development and
security and robustness issues.
So a couple of possible solutions:
- Training: teach peoples to write good code. Writing good code
doesn't take longer than writing poor code, it just requires more
skill.

I'd say different skills, rather than more skill. It actually
takes more skill to write correct C, but if you apply those
skills to C++, the results aren't going to be very good.
- Garbage collection: has clear advantages in some situations.
But is it a substitute for poor skills?

No. You need both (and the correct skills is the more important
issue).
- Remove flexibility from the C++ language: Is one of the
weakness of C++ its great flexibility? It brings as cost, the
capability of writing poor code which is somewhat limited in a
less flexible language. (witness for example python's
attempts to enforce "nice" formatting by making formatting and
whitespaces part of the syntax)

The reason C++ is widespread is not its elegance and its
simplicity. Nor even its safety, per se. The reason C++ is
widespread is its flexibility. And the fact that as the global
knowledge base expands, we learn new and better ways of doing
things. So that, correctly written, C++ is considerably more
robust and secure than Java, despite Java's having been designed
with those issues in mind. Java cast in stone the established
concepts of robustness and security at the time it was
developed; since then, we know more, and can do better. At
least in C++, because nothing has been cast in stone.
 
P

Pascal J. Bourguignon

I think you are unfairly highlighting cost of explicit allocation
while giving the garbage collector the right to run in a separate
thread "for free".

The total resource usage of an application is the total of all the
threads it runs, including garbage collector thread. The resources
used by the garbage collector thread are not available for other
usages. They haven't been created out of thin air "for free".

True. But my exactly transmitting the point of Intel's engineers who
forewarn us programmers that we will soon have a very big number of
cores at our disposal, and when you have more cores than threads to
run, processing becomes as free as the air (on a planet).

Multi threading on multi-core can under certain circumstance allows
one to make an application that is more responsive. However, this is
not reserved to garbage collected application. One can, and I have
written non-garbage collected multi-threaded applications

If we are forward looking, a lot of applications become
multi-threaded (or multi-process), regardless of if they are garbage
collected or not. So at that point, while a thread might be busy
freeing memory somewhere, another thread might be doing something
else. This is allowed regardless if it is garbage collected or not.

But the criteria, for the final user, is the response time, not the
percentage of core you can keep busy.

If I write... say a image/video processing application for multi
core processors aiming at maximum performance, I would write it so
that it uses all the cores availaible to their max capability, so
their wouldn't be an "unused" core that can be used to run the garbage
collector "for free".

Assume you have one core per pixel, each processing its own pixel, and
a few other cores free.

Is it better to spend in all the threads time in a complex allocator,
all in parallel, or is it better to have all the worker threads/cores
allocate in a few instructions, right aways, and leave the collection
of the garbage up to the free cores?

I grant you that garbage collection allows you to more easily try to
setup a system where memory clean up happens during dead time (low
load time).

Even without low load times. That's the point of parallel garbage
collectors in multiprocessors.

However, in practice, it is not uncommon while using some application
to notice that garbage collection is happening just at the "wrong
time" and is using a lot of resource when one would rather they were
not used for garbage collection at that particular moment. If garbage
collection is such a silver bullet, why is this happening?

Well, given that we have to wait on all the applications, and most of
them don't have a garbage collector, I don't see in my experience that
the situation is any better without them. But again, this is already
become very old history, given the advent of massive multicore
processors, and the effective implementation of parallel garbage
collectors.

This is also the direction Apple's following.
 
B

Bo Schwarzstein

i see serveral source codes , and i found they almost only use "new"
and "delete" keywords to make they object.
Why should i do that , and as i know the object is going to be destroy
by itself at the end of the app

for example:
class test
{
  public:
       int x;

}

int main(int argc, char **argv)
{
  test *n= new test;
  .
  .
  ...
  delete n;

return 0;}

i know that the object created this way is in the heap which have much
memory than stack but why they always define objects that way , why
not just say "test n" and the object will be destroyed by itself at
the end of the program! , instead of using "new" and maybe u will
forget to "delete" at the end

It's a problem about design. You can write a smart pointer, wrap the
'new'/'delete' operations in constructor/deconstructor functions, the
compiler will add them in your function automatic.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,787
Messages
2,569,627
Members
45,329
Latest member
InezZ76898

Latest Threads

Top