Re: Deleting and checking pointers
Seungbeom Kim wrote:
kanze wrote:
Every coding guidelines I've seen has forbidden using the
implicit conversions to bool. They also say, yes, to limit
the lifetime of variables as much as possible, and above
all, don't declare variables before they can be correctly
initialized. But the first is a relative thing, which can't
outweigh an absolute prohibition, and the second, more
important part, is met in both cases.
There are, of course, a few major exceptions, generally
based on idioms so common they stick out, and won't be
overlooked -- things like:
while ( std::getline( ... ) ) ...
(In practice, the presence of a << in the conditional works
almost like a red flag -- you expect multiple side effects.)
Perhaps the cases involving dynamic_cast to initialize a
pointer will become one of these exceptions -- to date,
dynamic_cast isn't used enough in my code for anything
involving it to be considered an "established idiom".
Don't you think that it's an idiom common enough to return a
non-null pointer just to mean true and a null pointer just to
mean false?
You mean like the iostreams stuff, where it is forbidden to
dereference the non-null pointer as well, the reasons for using
pointers and not bool are historical (and linked to the fact
that C++ supports lossy conversions).
In such cases, the pointer is considered a logical equivalent of
bool.
operator void*() const
{
if (fail()) return 0; else return this;
}
Does every coding guidelines you've seen forbid something like this:
if (object) // ...
and recommend this:
if (object != NULL) // ...
or this:
if (static_cast<bool>(object)) // ...
..?
No. Not in this particular case. Because conceptually, the
type is boolean, even if the formal type is a pointer. It has
two "values", although one of the values (both, formally) may
take on many different bit patterns. Many of the coding
guidelines in question go back to before bool; some were even C,
before I started using C++. The basic rule was always to write
what you meant -- if the type in question was conceptually bool,
you wrote "if ( var )", if it was conceptually int, "if ( var !=
0 )", if it was conceptually a pointer, "if ( var != NULL )",
and if it was conceptually a character, "if ( var != '0' )".
Just because the language confuses things doesn't mean that you
have to.
Quoting Stroustrup: 'For a pointer p,
if (p) // ...
is a *direct* statement of the test "does p point to a valid
object (assuming proper initialization),"
Except that that is NOT what it is saying. It is saying "is p
true"? And if p is a pointer, this is a senseless statement.
What does it mean for a pointer to be true ?
whereas
if (p != 0) // ...
states the same question *indirectly* by comparing to a value
known not to point to an object.' (emphasis mine)
That's not the general point of view I've seen. A pointer is
NOT a yes or no value. It has more than two possible legal
values. You are testing for a special, sentinal value -- one of
many possibilities -- is it equal to this sentinal value.
And of course, most of the coding guidelines insisted that you
use NULL for pointers (and '\0' for characters -- and often 0.0
for floating point, but that one wasn't systematic).
This may be a matter of preference, but I don't think 'if
(pointer)' is so bad enough to deserve being forbidden by most
coding guidelines.
It's more than simple preference -- it's a question of saying
what you mean. If you know that you are testing against a
specific sentinal value, why do you want to hide this fact from
the reader. (Obviously, if you use 0 instead of NULL, the
argument bears less weight, since both forms contain a little
obfuscation. But I still prefer a comparison with 0 to an
implicit, behind the back conversion.)
// This doesn't work with standard containers, unfortunately.
if( Element* fou = container.find(barre))
frobnicate(fou);
But what's the difference between that and:
Element* fou = container.find( barre ) ;
if ( fou != NULL ) ...
Except that the latter is considerably more readable.
Maybe only for those who are not familiar with the C++
declare-in- condition construct. :)
Yah. For those like myself who only have 15 years experience
with C++, and 25 with C and C++ together. I find it confusing.
And I think I know C++ well enough to be considered a
reasonably competent programmer in the language.
Actually, Stroustrup refers to the ability to declare a
variable in a condition as "one of the most elegant
applications of these two principles [of introducing a
variable into the smallest scope possible to avoid accidental
its misuse, and of delaying the definition of a local variable
until one can give it an initial value]."
And I disagree with him. I find that globally, it is a
beautiful tool for obfuscation -- for cramming as much as
possible into a single line, without regards for the reader. A
move in the direction of APL, and write only code.
Another difference between the two examples, which I think is
far more important, is that you cannot accidentally use fou
when it is null. Especially important for dynamic_cast:
Base* pb = ...;
if (Derived* pd = dynamic_cast<Derived*>(pb)) {
// you can use pd
}
else {
// and here
}
// you cannot use pd here
I don't buy it. Sure you can use pd after the "if" if you
declare it separately. Just like you can use it in the else
case of the "if". Of the two, the else case is by far the more
likely -- how often do you have code in the function after such
an if?
The argument just doesn't hold water.
It's like a static analysis enforced by the compiler. In fact,
my coding guideline would be to put every dynamic_cast in a
declaration in a condition so that you could never use invalid
pointers by mistake.
Except that you can, since the pointer is just as accessible in
the else branch.
My coding guideline here is to keep the functions short enough
and simple enough that you can see all of the uses easily, and
so that you won't get confused enough to use the pointer when it
isn't valid.
It's probably just because dynamic_cast itself is not very
commonly used that you don't consider it to be an "established
idiom".
That's possible. Or simply because it is new. I'm willing to
admit that this could become an established idiom -- perhaps it
already is in some shops. In which case, it becomes another
special case, one which every C++ programmer immediately
recognizes. And thus, one which doesn't hinder readability too
much. (Not using an established idiom when one applies has
other disadvantages. For example, I personally think that:
file >> whatever ;
if ( ! file.failed() ) ...
cleaner than:
if ( file >> whatever )...
But if I actually saw it in code, I'd loose some time wondering
why the programmer did it that way, rather than using the
standard idiom.)
I've never used a C++ compiler where declaring the variable
in the for statement didn't work. Throughout most of my C++
experience, of course, the scope of the variable didn't end
with the for loop, but I've yet to see where this really
changed anything.
You've never tried anything like
for (int i = 0; i < M; ++i) // ...
// ...
for (int i = 0; i < N; ++i) // ...
in a same scope, throughout most of your C++ experience?
No. Generally speaking, a single function will not have more
than a single for loop (except for nested for loops, of course).
You mean you always use different names for different loop
variables?
That's also generally a requirement in most coding guidelines.
Don't use the same name for two different variables within a
single function. It generally leads to confusion. Admittedly,
the rule was conceived more in view of nested scopes -- having a
nested variable hide an outer variable is very confusing. But
then, the authors' of the guidelines probably didn't conceive of
anyone writing a function with two successive for loops.
--
James Kanze GABI Software
Conseils en informatique orient?e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S?mard, 78210 St.-Cyr-l'?cole, France, +33 (0)1 30 23 00 34
I'm amazed. :)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]