I
Ian Collins
The copy is made before the increment, so RVO does not apply.Phlip said:(Not reading the whole thread here but) doesn't return value optimization
apply?
The copy is made before the increment, so RVO does not apply.Phlip said:(Not reading the whole thread here but) doesn't return value optimization
apply?
AFAIUI, the "2's complement" part is not the requirement. The actual
representation is implementation-defined.
I am not sure about this one. 'long' is required to be at least 32
bits, so if your implementation provides 'long' (as it should to be
a standard C++ compliant implementation), so 'int32_t' would be
typedef'ed to 'long', and provided no matter what. And 'int64_t'
could probably be emulated (using some class). C++ has no limitations
C99 has when type implementation is concerned.
We are talking C++ here, not C99.
Why not? Worked well for empty structs, the ternary operator...
V
1) Should one prefer short / long to int in favour of better
compability? Does the sizeof(int) still vary dramatically between
platforms?
2)Should one check a pointer for NULL before deleting it? Although
deleting a NULL pointer is said to be safe in C++, I see a lot of code
doing the check before deletion. Is this because they still preserve
the C attitude?
3) When is a struct favourable over a class?
4) Should one favor '++iterator' over 'iterator++'? I did some
performance tests myself and did not see a big gap between their
execution times ( by using containers with size 10000000 which contain
objects )
AFAIUI, the "2's complement" part is not the requirement. The actual
representation is implementation-defined.
I am not sure about this one.
'long' is required to be at least 32
bits, so if your implementation provides 'long' (as it should to be
a standard C++ compliant implementation), so 'int32_t' would be
typedef'ed to 'long', and provided no matter what.
And 'int64_t' could probably be emulated (using some class).
C++ has no limitations C99 has when type implementation is
concerned.
We are talking C++ here, not C99.
sizeof(int) is implementation-defined and hence no conclusions
can be drawn (rather, it is incorrect to draw any conclusions)
regarding whether it varies across platforms and/or
implementations.
According to the C++ standard, deleting a NULL pointer is
safe. So it is not mandatory to test the pointer for NULL
before deleting it.
If you have all public members and you want to avoid writing
"public" access specifier in the definition:
Preincrements are generally faster than postincrements since
postincrements need to make a copy before doing an increment. No such
copy-construction is necessary for pre-increment.
Post-increment always performs copying. So there is always difference.
Sure, you won't see it for integers etc.
Always use pre-increment except situation when you really need pre-
increment.
The copy is made before the increment, so RVO does not apply.
Victor said:We are talking C++ here, not C99.
Why not? Worked well for empty structs, the ternary operator...
D. Susman said:
2)Should one check a pointer for NULL before deleting it? Although
deleting a NULL pointer is said to be safe in C++, I see a lot of code
doing the check before deletion. Is this because they still preserve
the C attitude?
3) When is a struct favourable over a class?
4) Should one favor '++iterator' over 'iterator++'? I did some
performance tests myself and did not see a big gap between their
execution times ( by using containers with size 10000000 which contain
objects )
Use int, and (per a recent thread here) if you are not
portable, don't waste time pretending you are portable. If
your boss requests portability, actually run and test on
multiple platforms as you code.
Your question contains the latent assumption that you can
never change your code once its 'int's are installed. If you
instead write lots of unit tests, they will check things like
the maximum values for your important numbers, and they will
help you change your variable types when the time comes.
Your remaining int awareness invests in writing tests that
check your program's boundaries.
No, you should use a smart pointer that wraps all such checks
up for you.
Next, the line delete(int*)NULL is well-formed and
well-defined to do nothing.
(And note you should use a C++ style cast, in the form
"elaborate_cast<int*>", instead of a simple (int*).)
It's because they don't fold duplication up into smart
pointers. _That_ is preserving the C attitude.
When you need a data bucket that is itself private to an outer class.
Favor ++it for almost exactly that reason. Specifically, if it
is a raw pointer the pre- and post-increments will be nearly
the same speed. But if you upgrade it into a big object, the
it++ form will create a new object, copy it, and throw it
away. The compiler might not be able to optimize this away.
Now use /el Goog/ and look up "premature optimization". When
you learn C++ you should start by learning clean code and good
design, and don't worry about performance until you find a
real need.
James said:D. Susman wrote: [snip]2)Should one check a pointer for NULL before deleting it?No, you should use a smart pointer that wraps all such checks
up for you.
Why? What does a smart pointer buy you, if all it does is an
unnecessary test?
Don't forget, too, that most delete's are in fact "delete this".
And "this" cannot be a smart pointer.
Kai-Uwe Bux said:Of all the 632 lines containing delete, only one (sic) read:
delete this;
So here is a question: given that uses cases frequencies can differ
dramatically, can one give rational general advice concerning smart
pointers? and if so, what would that advice be?
This from you? I thought you believed in testing.
Have you ever seen an actual case where the choice made a significant
difference in program runtime?
James said:Why? What does a smart pointer buy you, if all it does is anD. Susman wrote: [snip]
2)Should one check a pointer for NULL before deleting it?
No, you should use a smart pointer that wraps all such checks
up for you.
unnecessary test?
Don't forget, too, that most delete's are in fact "delete this".
And "this" cannot be a smart pointer.
Are you serious?
I venture the conjecture that this heavily depends on your
code base and on the problem domain.
So here is a question: given that uses cases frequencies can differ
dramatically, can one give rational general advice concerning smart
pointers? and if so, what would that advice be?
James said:James said:D. Susman wrote: [snip]
2)Should one check a pointer for NULL before deleting it?
No, you should use a smart pointer that wraps all such checks
up for you.
Why? What does a smart pointer buy you, if all it does is an
unnecessary test?
Don't forget, too, that most delete's are in fact "delete this".
And "this" cannot be a smart pointer.Are you serious?
Yes. Most (not all) objects are either values or entity
objects. Value objects aren't normally allocated dynamically,
so the question doesn't occur. And entity objects usually (but
not always) manage their own lifetime.
And your style. If you're trying to write Java in C++, and
dynamically allocating value objects, then it obviously won't be
true.
If you're writing well designed, idiomatic C++, then a
large percentage of your deletes probably will be "delete this".
[...]So here is a question: given that uses cases frequencies can differ
dramatically, can one give rational general advice concerning smart
pointers? and if so, what would that advice be?
The only "rational" advice would be to use them when
appropriate. Which depends a lot on context---if you're
using the Boehm collector, for example, you'll probably need
them less than if you aren't. But on the whole, even without
the Boehm collector, I doubt that they'd represent more than
about 10% of your pointers in a well designed application.
They obviously don't apply to entity objects, whose lifetime
must be explicitly managed. And how many other things would you
allocate dynamically?
Most of the time I see a lot of smart
pointers, it's for things that shouldn't have been allocated
dynamically to begin with.
Kai-Uwe Bux said:James Kanze wrote:Yes. Most (not all) objects are either values or entityJames Kanze wrote:
D. Susman wrote:
[snip]
2)Should one check a pointer for NULL before deleting it?
No, you should use a smart pointer that wraps all such checks
up for you.
Why? What does a smart pointer buy you, if all it does is an
unnecessary test?
Don't forget, too, that most delete's are in fact "delete this".
And "this" cannot be a smart pointer.
Are you serious?
objects. Value objects aren't normally allocated dynamically,
so the question doesn't occur. And entity objects usually (but
not always) manage their own lifetime.
Most of my dynamically allocated objects are used to implement container
like classes (like a matrix class), wrappers like tr1::function, or other
classes providing value semantics on the outside, but where the value is
encoded in something like a decorated graph.
The internally allocated nodes do not manage their own lifetime: they are
owned by the ambient container/wrapper/graph.
I have no idea about Java. My code is heavily template based,
uses value semantics 95% of the time, and new/delete is rather
rare (about one delete in 500 lines of code).
In my codebase, the lifetime of an object is managed
by the creator, not by the object itself.
Ownership almost never is transfered. The reason that the
object is created dynamically is, e.g., that its size was
unknown (in the case of an array) or that a client asked for
an entry to be added to a data structure.
I disagree. Could it be that you are thinking of object oriented designs?
[...]The only "rational" advice would be to use them whenSo here is a question: given that uses cases frequencies can differ
dramatically, can one give rational general advice concerning smart
pointers? and if so, what would that advice be?
appropriate. Which depends a lot on context---if you're
using the Boehm collector, for example, you'll probably need
them less than if you aren't. But on the whole, even without
the Boehm collector, I doubt that they'd represent more than
about 10% of your pointers in a well designed application.
That depends on what you count as a smart pointer. E.g.,
tr1::function or boost::any are very close to smart pointers
with copy semantics. However, it clearly does not compete with
pointers.
However, by and large, I also found that (smart) pointers
rarely ever make it into client code. When I put a class in my
library, it usually provides value semantics, and in fact,
most of my classes do not have virtual functions or virtual
destructors.[1] Thus, client code has no reason to use dynamic
allocation.
A whole lot. E.g., very often in math programming, I find myself dealing
with _values_ that are best represented by trees, pairs of trees, trees
with some decoration, or graphs. Implementing those classes requires a
whole lot of dynamic allocation, but in the end that is just some means to
realize a class that has value semantics from the outside. The objects are
then in charge of destroying the internal nodes whose graph structure
encodes the mathematical value of the object. Leaving that to smart
pointers is very helpful in prototyping.
I cannot refute that observation. However, that is a function
of the code you are looking at.
That's a strange thing to say, most code bases I've worked with and inJames said:Curious. Not necessarily about the value semantics; if you're
working on numerical applications, that might be the rule. But
templates at the application level? Without export, it's
unmanageable for anything but the smallest project. (The
companies I work for tend to ban them, for a number of reasons.)
Ian said:James Kanze wrote:
That's a strange thing to say, most code bases I've worked with and in
my own, templates are widespread. I don't see where unmanageable comes
from.
Then again, I can't remember the last time I used "delete
this", the current fairly large project I'm working with
doesn't use it at all.
James said:Kai-Uwe Bux said:James Kanze wrote:James Kanze wrote:
D. Susman wrote:
[snip]
2)Should one check a pointer for NULL before deleting it?
No, you should use a smart pointer that wraps all such checks
up for you.
Why? What does a smart pointer buy you, if all it does is an
unnecessary test?
Don't forget, too, that most delete's are in fact "delete this".
And "this" cannot be a smart pointer.
Are you serious?
Yes. Most (not all) objects are either values or entity
objects. Value objects aren't normally allocated dynamically,
so the question doesn't occur. And entity objects usually (but
not always) manage their own lifetime.Most of my dynamically allocated objects are used to implement container
like classes (like a matrix class), wrappers like tr1::function, or other
classes providing value semantics on the outside, but where the value is
encoded in something like a decorated graph.The internally allocated nodes do not manage their own lifetime: they are
owned by the ambient container/wrapper/graph.
That is one of the cases where "delete this" would not be used.
But it accounts for how many delete's, in all? (Of course, in a
numerics application, there might not be any "entity" objects,
in the classical sense, and these would be the only delete's,
even if they aren't very numerous.)
Curious. Not necessarily about the value semantics; if you're
working on numerical applications, that might be the rule. But
templates at the application level? Without export, it's
unmanageable for anything but the smallest project. (The
companies I work for tend to ban them, for a number of reasons.)
There are cases where it is appropriate. There are also cases
where the lifetime will be managed by some external entity such
as the transaction manager.
O.K. That's a case that is almost always handled by a standard
container in my code. I have entity objects which react to
external events.
More to the point, I'm thinking of commercial applications. I
sort of think you may be right with regards to numerical
applications.
[...]
So here is a question: given that uses cases frequencies can differ
dramatically, can one give rational general advice concerning smart
pointers? and if so, what would that advice be?
The only "rational" advice would be to use them when
appropriate. Which depends a lot on context---if you're
using the Boehm collector, for example, you'll probably need
them less than if you aren't. But on the whole, even without
the Boehm collector, I doubt that they'd represent more than
about 10% of your pointers in a well designed application.That depends on what you count as a smart pointer. E.g.,
tr1::function or boost::any are very close to smart pointers
with copy semantics. However, it clearly does not compete with
pointers.
I'm not sure I agree. I'd be tempted to say that if you can't
dereference it, it isn't a smart pointer.
STL iterators are
smart pointers because they support dereferencing. Still, in a
commercial application, *most* pointers are used for navigation
between entity objects. You rarely iterate; you recover the
real pointer from the return value of std::map<>::find almost
immediately, etc.
However, by and large, I also found that (smart) pointers
rarely ever make it into client code. When I put a class in my
library, it usually provides value semantics, and in fact,
most of my classes do not have virtual functions or virtual
destructors.[1] Thus, client code has no reason to use dynamic
allocation.
Are you writing libraries?
Obviously, something like
std::vector<> won't use delete this for the memory it manages.
Something that primitive probably won't use a classical smart
pointer, either, but I guess more complex containers might.
In the applications I work on, of course, such low level library
code represents something like 1% or 2% of the total code base.
And for the most part, we don't write it; the standard
containers are sufficient (with wrappers, in general, to provide
a more convenient interface).
I think that's the difference. I guess you could say that my
code also contains a lot of trees or graphs, but we don't think
of them as such; we consider it navigating between entity
objects---the objects have actual behavior in the business
logic. And the variable sized objects (tables, etc.) are all
handled by standard containers.
Certainly. I also see a lot of code in which there is only one
or two deletes in a million lines of code; the value types are
all copied (and either have fixed size, or contain something
like std::string), and the entity types are managed by a single
object manager. In many cases, the architecture was designed
like this just to avoid "delete this", but the delete request to
the object manager is invoked from the object that is to be
deleted---which means that it's really a delete this as well.
Kai-Uwe Bux said:James said:That is one of the cases where "delete this" would not be used.Kai-Uwe Bux said:James Kanze wrote:
James Kanze wrote:
D. Susman wrote:
[snip]
2)Should one check a pointer for NULL before deleting it?
No, you should use a smart pointer that wraps all such checks
up for you.
Why? What does a smart pointer buy you, if all it
does is an unnecessary test?
Don't forget, too, that most delete's are in fact
"delete this". And "this" cannot be a smart pointer.
Are you serious?
Yes. Most (not all) objects are either values or entity
objects. Value objects aren't normally allocated
dynamically, so the question doesn't occur. And entity
objects usually (but not always) manage their own
lifetime.
Most of my dynamically allocated objects are used to
implement container like classes (like a matrix class),
wrappers like tr1::function, or other classes providing
value semantics on the outside, but where the value is
encoded in something like a decorated graph.
The internally allocated nodes do not manage their own
lifetime: they are owned by the ambient
container/wrapper/graph.
But it accounts for how many delete's, in all? (Of course, in a
numerics application, there might not be any "entity" objects,
in the classical sense, and these would be the only delete's,
even if they aren't very numerous.)
It's not just numerics. But numerics applications are
definitely a very good example of what I had in mind. I think
that a lot of scientific computing looks like this.
As you can see, it' just a trivial filter; and all the real
code is in the library. That, in turn, is templated for
flexibility. E.g., the matrix class is supposed to work just
as nicely with infinite precision integers, and an algorithm
picking out the maximal elements (with respect to some partial
order) from a sequence should be generic.
As you have figured, it is somewhat like number crunching
(except that I am dealing more with topological and
combinatorial algorithms, so enumerating all objects of a
given size and type is a typical thing that happens in my
code).
Now, with respect to huge applications, I see that templates
are an issue. On the other hand, I thought, that is what
nightly builds are for: You have a bug to fix, you locate it,
you add a unit test for the failing component that displays
the bug without using all the unrelated crap from the huge
ambient application; and then you work on that component until
it passes all tests. After a commit to the code base, the huge
application is rebuilt over night and all automatic tests are
run. Working on your component in isolation, you still have
short edit-compile-test cycles.
By "commercial", do you mean "software for sale" or "software
used in the sales department"
I agree that programs that act in complex environments and
have to respond to thousand different kind of events will use
objects to model the world they operate in (I am thinking of
transactions between banks, simulations, GUI, games, etc). On
the other hand, programs that perform highly complicated
transformations in batch mode are likely to be different.
That would go for most of number crunching, scientific
programming, compilers, symbolic computation, combinatorial
optimization, and so on. I expect the code for those to be
more similar to mine than to yours. There are programs for
sale in all these categories (but I would not expect a typical
sales department to make heavy use of a PDE solver).
I think the difference is not commercial versus
non-commercial, but more whether your application is
event-driven or has the classical (ancient?)
parse_input....write_output format.
Think of a smart pointer that does not interfere with life
time but helps with the typical problems when pointers are
used for navigation. E.g., you can wrap the observer pattern
into a smart pointer so that all those objects that have a
handle to a potentially suicidal one get notified just before
it jumps off the cliff.
I don't really like smart pointers there either.
However, they are really handy in getting a prototype up and
running, which is a good thing during the design phase when
you are experimenting with the interface and whip up the
initial test cases. When the design is stabilizing, I tend to
first replace smart pointers (and raw pointers) by pointer
wrappers that support hunting double deletion and memory
leaks, and finally by pointer wrappers that wrap new and
delete and provide hooks for an allocator to be specified by
the client code.
I forgot to mention one other reason to use T* instead of T:
in template programming, the first is available for incomplete
T. For instance, there are two obvious implementation of the
box container (a box can be empty or contain a single item; I
think such a container is sometimes called fallible or
optional). One implementation has a T* data field and the
other has a T data field. The first will work with incomplete
T the second won't. When doing template programming, one has
to be aware of the conceptual requirements created by an
implementation approach. Sometimes, that forces or suggests
dynamic allocation.
I did not want to argue for or against "delete this". I can
see how this idiom is useful. I was just flabbergasted by your
claim that most deletes are of this form. But now, I can see
where you were coming from.
However, it is somewhat funny that "delete this" looks scary
enough that people invent roundabout ways to avoid it.
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.