Problem:
My own default constructor is considered to be *initializing the
variable* (even though it doesn't), whereas the original one does
not. Thus, when I declare and use it before initializing it, the
compiler no longer warns me.
Question:
Are there any compiler settings (even compiler specific ones; I am
using MSVC++) that state MY default constructor behaves exactly like
the regular default constructor?
Thanks for your time,
Jason
P.S. My default constructor could initialize the variable to all 0's,
but this has two unwanted effects: 1. It slows down code in which
this initialization needn't occur. 2. It potentially hides bugs that
would show up if it were left uninitialized as it should be. (This is
similar to how MSVC++'s debugger initializes all variables to 0, which
is silly, since it should initialize them to randomness -- as will
happen in the release build -- to make bugs appear as quickly as
possible).
That's your choice, isn't it? You've chosen to implement your c-tor
(which is supposed to initialise the members) in such a way that does
*not* do what it promises to do. So, why are you complaining? It is
not a problem, or at least it's very easy to solve, isn't it?
>
> Question:
> Are there any compiler settings (even compiler specific ones; I am
> using MSVC++) that state MY default constructor behaves exactly like
> the regular default constructor?
Not that I know of.
>
> Thanks for your time,
> Jason
>
> P.S. My default constructor could initialize the variable to all 0's,
> but this has two unwanted effects: 1. It slows down code in which
> this initialization needn't occur.
Can you share the numbers, how much *does* it actually "slow down
the code"?
> 2. It potentially hides bugs that
> would show up if it were left uninitialized as it should be.
Written *correctly* it _prevents_ bugs, not hides them.
> (This is
> similar to how MSVC++'s debugger initializes all variables to 0, which
> is silly, since it should initialize them to randomness -- as will
> happen in the release build -- to make bugs appear as quickly as
> possible).
Not similar at all. When in the "debug" build variables are given
some values whereas in the "non-debug" they are not, you have a very
big problem if your code ever depends on this. When your default
c-tor initialises member variables (to 0 or whatever) *always*, there
is no randomness, and you can *rely* on the initialisation happening.
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
>Situation:
>I have a simple struct that, say, holds a color (R, G, and B). I
>created my own constructors to ease its creation. As a result, I lose
>the default constructor. I dislike this, but it's easy to solve: I
>just make my own default constructor.
>
>Problem:
>My own default constructor is considered to be *initializing the
>variable* (even though it doesn't), whereas the original one does
>not. Thus, when I declare and use it before initializing it, the
>compiler no longer warns me.
>
>Question:
>Are there any compiler settings (even compiler specific ones; I am
>using MSVC++) that state MY default constructor behaves exactly like
>the regular default constructor?
No.
>P.S. My default constructor could initialize the variable to all 0's,
>but this has two unwanted effects: 1. It slows down code in which
>this initialization needn't occur.
Almost certainly by an imperceptible degree.
>2. It potentially hides bugs that
>would show up if it were left uninitialized as it should be. (This is
>similar to how MSVC++'s debugger initializes all variables to 0, which
>is silly, since it should initialize them to randomness -- as will
>happen in the release build -- to make bugs appear as quickly as
>possible).
The debugger doesn't do that, and AFAIK, never has. (I've been hearing this
for many years, and I still don't know how this rumor got started.) When
certain debug options are in effect, the compiler will initialize locals to
certain non-zero patterns.
--
Doug Harrison
Visual C++ MVP
> Jason Doucette wrote:
>> Question:
>> Are there any compiler settings (even compiler specific ones; I am
>> using MSVC++) that state MY default constructor behaves exactly like
>> the regular default constructor?
>
> Not that I know of.
For the future, though, C++0x will let you do this:
struct S
{
S() = default;
S(int);
};
with the effect that the compiler will generate that default
constructor as if you hadn't declared the other one.
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)
I don't believe so.
>
> Thanks for your time,
> Jason
>
> P.S. My default constructor could initialize the variable to all 0's,
> but this has two unwanted effects: 1. It slows down code in which
> this initialization needn't occur.
How likely is this to happen? In correctly written code, most
creatíons will have a sensible value available. I doubt that not
having a proper initialisation will slow down your program in a
measurable way.
But if you do have such a beast hanging around, you could do something
aka:
struct uninitialised_t
{
};
class rgb
{
...
rgb( uninitialised_t ) {}
...
};
> 2. It potentially hides bugs that
> would show up if it were left uninitialized as it should be.
Hmmm... having a value of all zeroes does not look that stupid to me.
> (This is
> similar to how MSVC++'s debugger initializes all variables to 0, which
> is silly, since it should initialize them to randomness -- as will
> happen in the release build -- to make bugs appear as quickly as
> possible).
No: randomness is most often not a good solution. But I agree that a
value different from 0 might enable you to spot those errors sooner.
But why so many uninitialised variables in the first place? My
experience is that uninitialised variables always are output
parameters and in that case, they are typically filled in the very
first statement after their definition.
/Peter
[ ... ]
> P.S. My default constructor could initialize the variable to all 0's,
> but this has two unwanted effects: 1. It slows down code in which
> this initialization needn't occur. 2. It potentially hides bugs that
> would show up if it were left uninitialized as it should be. (This is
> similar to how MSVC++'s debugger initializes all variables to 0, which
> is silly, since it should initialize them to randomness -- as will
> happen in the release build -- to make bugs appear as quickly as
> possible).
I'd go a step further: instead of leaving the values uninitialized, I'd
create a version of the class specifically for debugging:
class color {
int r_, g_, b_;
bool initialized_;
public:
color()
: initialized_(false)
{}
color(int r, int g, int b)
: r_(r), g_(g), b_(b), initialized_(true)
{}
int red() {
assert(initialized_);
return r_;
}
int green() {
assert(initialized_);
return g_;
}
int blue() {
assert(initialized_);
return b_;
}
operator unsigned long() {
assert(initialized_);
return r_ << 16 | g_ << 8 | b_;
}
};
While the compiler no longer warns you about an uninitialized variable,
this still gives you a run-time assertion, which is often nearly as
useful.
--
Later,
Jerry.
The universe is a figment of its own imagination.
No, it's not my choice. I do *not want* to make a default constructor
myself. I want to keep the original default constructor that does no
initialization. But, C++ is forcing me to make my own.
> > P.S. My default constructor could initialize the variable to all 0's,
> > but this has two unwanted effects: 1. It slows down code in which
> > this initialization needn't occur.
>
> Can you share the numbers, how much *does* it actually "slow down
> the code"?
I have no numbers. But it may be in code that is run 100's of
billions of times. It's probably not that big of a deal in comparison
to total program time, so, I'm not concerned. (My thought was, "Why
do it if you don't have to?") The second issue was my main concern:
> > 2. It potentially hides bugs that
> > would show up if it were left uninitialized as it should be.
>
> Written *correctly* it _prevents_ bugs, not hides them.
Forcing everything through a constructor to ensure all objects are
initialized should prevent bugs, yes.
But, in some cases, I have these color structs that are only
initialized *after* they are declared. So, the compiler tells me when
I'm being stupid and using them before they are initialized. I lose
this warning when I am forced to make my own default constructor,
since it thinks I am initializing on declaration, which I'm not. This
means I have to make the default constructor initialize it, before I
even know what to initialize it to. So, now, when I use it before
it's 'really' initialized, I get no warning.
So, the only real solution is that I must treat this "somewhat-native
style" type as a true class, where whenever it is declared, it must be
initialized. It may be cumbersome to do this. I liked thinking about
this type as a native data type, not as a class. :(
> Not similar at all. When in the "debug" build variables are given
> some values whereas in the "non-debug" they are not, you have a very
> big problem if your code ever depends on this. When your default
> c-tor initialises member variables (to 0 or whatever) *always*, there
> is no randomness, and you can *rely* on the initialisation happening.
Debug builds that zero out everything will hide bugs in code that
works properly if all variables are zero'ed out, but doesn't work
properly if all variables are set to random values. Unfortunately,
when the code is run on customer's machines, they are running in the
Release build which is using the random values. That's why Debug
should forcibly randomize all unset values to force bugs to appear,
and force the programmer to make sure his code is legitimate. (Note
that I am talking about native data types.)
Jason
Agreed.
> >2. It potentially hides bugs that
> >would show up if it were left uninitialized as it should be. (This is
> >similar to how MSVC++'s debugger initializes all variables to 0, which
> >is silly, since it should initialize them to randomness -- as will
> >happen in the release build -- to make bugs appear as quickly as
> >possible).
>
> The debugger doesn't do that, and AFAIK, never has. (I've been hearing this
> for many years, and I still don't know how this rumor got started.) When
> certain debug options are in effect, the compiler will initialize locals to
> certain non-zero patterns.
You are quite right. MSVC++ sets them all to C's in hex, so you can
easily see that they are uninitialized in the debugger window. I
already knew this, as I tested it with a function I made to show the
bits of any data type, so I don't know why I keep thinking they are
set to 0.
Jason
Wow, Pete, that's cool. C++0x is the planned new C++ standard, so, I
guess I'm not the only one who wants this functionality.
Jason
The reason that MSVC sets them to all 0xcc is not for easy visibility
(though that's a nice side-benefit). It's because 0xcc is the INT3
opcode in x86, which breaks to the debugger.
The slow down is likely imperceivable, but my thought was, why
initialize a variable twice when it only has to be initialized once?
> But if you do have such a beast hanging around, you could do something
> aka:
> struct uninitialised_t
> {
> };
>
> class rgb
> {
> ...
> rgb( uninitialised_t ) {}
> ...
> };
Thanks for the tip. The issue was just that I enjoyed the compiler
letting me know when I'd use a variable before it was initialized:
int i;
int a = i + 5;
It's not a big deal, but it helps catch a few bugs. Now, if all int's
are forced to run my own default constructor that sets them to 0, then
the above bug is not caught (unless all code that declares int's
*wants* them to be init'ed to 0.)
> No: randomness is most often not a good solution. But I agree that a
> value different from 0 might enable you to spot those errors sooner.
> But why so many uninitialised variables in the first place? My
> experience is that uninitialised variables always are output
> parameters and in that case, they are typically filled in the very
> first statement after their definition.
I don't have many at all. I declare variables right where they are
used, and thus they are initialized on the spot. Certain unique
algorithms prevent this, but not often.
I'm curious, why is randomness most often not a good solution?
Jason
Thanks for the tip, Jerry!
Jason
Ah! Thanks! :)
Jason
Should do this? Maybe-- such a thing being unavoidable would merely
annoy those people that can write good code from day 1. Will it do it?
Very darn unlikely. The current default behavior of *optional*
filling to fixed values is probably going to be as good as you get out
of the box.
If you want to check that variables are initialized before use,
there are a few options: (1) ramp warning level to 4, and the compiler
will check most local variables for use w/o initialization. (2) For
classes, add in a constructor in debug builds (only) that checks for
initialization. (3) Pay $$$ for a program like Boundschecker that'll
make a super-special debug build that does runtime checks for
uninitialized variables. (If building for linux, Valgrind is supposed
to be able to do this at a much reduced price.) (4) Run your program
thru static code checkers, like Coverity, that'll do an amazing job of
finding codepaths under which local variables aren't initialized. (Far
better than warning level 4).
Short summary of things: there's a few ways you can try and find
problems like the above for free, and several ways you can spend $$$
and find them faster. As lots of people have mentioned, your
insistence on shaving a few ms in release builds is probably hurting
you. You can very easily write some code that does a bunch of checks
in debug, and goes away in release builds.
Nathan Mates
--
<*> Nathan Mates - personal webpage http://www.visi.com/~nathan/
# Programmer at Pandemic Studios -- http://www.pandemicstudios.com/
# NOT speaking for Pandemic Studios. "Care not what the neighbors
# think. What are the facts, and to how many decimal places?" -R.A. Heinlein
> No, it's not my choice. I do *not want* to make a default constructor
> myself. I want to keep the original default constructor that does no
> initialization. But, C++ is forcing me to make my own.
You can write a do-nothing constructor, but even that will be heavier
than not having a constructor at all:
struct C
{
C() { }
...
This is the most lightweight solution you can do in C++ if you already
have another constructor. Of course the compiler might add some implicit
function calls to it. However, if your structure only contains POD
types, they will remain uninitialized. There's a good chance the
compiler can inline this for you, and remove the empty body.
Another solution to consider: simply don't write your own constructor at
all. Create a static function, or a non-member function to initialize
your data. Example:
struct Color
{
int r, g, b;
static Color Create(int ar, int ag, int ab)
{
Color c;
c.r = ar;
c.g = ag;
c.b = ab;
return c;
}
};
This has to perform two assignment operations for each member, though.
If performance is really so important for you, you might want to use a
less convenient method:
inline void InitColor(Color& c, int ar, int ag, int ab)
{
c.r = ar;
c.g = ag;
c.b = ab;
}
The compiler will likely inline this for you in release builds. And
there's always a possibility of using a macro (ouch!).
Otherwise my tip would be to avoid early optimizations, and optimize
only after doing some benchmarking. Did you know that calling malloc
just once can be a thousand times slower than initializing those
integers with 0? There is a possibility that you're trying to optimize
something that's not likely to be anywhere near the bottleneck.
Tom
But debug build does exactly that. I don't know why you think that
debug build zeroes out "everything". Exactly the opposite is true:
debug build will initialize everyhting with non-zero values by
default in order to _avoid_ accidental zeros.
"WFC Technical Note 006 - Funny Memory Values"
http://www.samblackburn.com/wfc/technotes/WTN006.htm
Alex
Well, except for the random part. The value the debug build uses is fixed,
but carefully chosen so it isn't a valid pointer, etc.
The optimizer should generate the same code for both versions, no need to
use the clunkier one (and in fact, the first one is necessary for
const-correctness). This is the Named Return Value Optimization (NVRO).
As well as being an invalid pointer (it's in the top 1GB of address space
which is reserved for the kernel, attempts to access it from user-mode will
cause an access violation).
True, the MSVC++ compiler already fills them with C's, so that'll
catch most issues. And the MSVC++ compiler's run time checker stops
the code dead as soon as you access it, so that catches ALL issues.
(Keep in mind I wrote the above while still under the false impression
that it just 0's them out, and the run-time checker doesn't halt on
use of uninitialized variables.)
> If you want to check that variables are initialized before use,
> there are a few options: (1) ramp warning level to 4, and the compiler
> will check most local variables for use w/o initialization.
That's the first thing I do for any project! :) (It appears MSVC++
IDE remembers this for subsequent projects, or perhaps this is its
default, which is great!)
> (2) For
> classes, add in a constructor in debug builds (only) that checks for
> initialization.
Good tip.
However, in *my* particular case, my color struct is more of a native
data type than a class. So, it's used everywhere, and such checks
would really, really slow down the Debug builds. (Imagine having a
Debug check on each initialization and use of "int". This would be
beyond reason for such a type.) I *agree* that this is a good tip for
most classes, just not native data types. For them, having the
compiler warn about use before initialization is more than fine, which
is exactly the thing I'm griping about losing when C++ forces me to
create my own default constructor.
> (3) Pay $$$ for a program like Boundschecker that'll
> make a super-special debug build that does runtime checks for
> uninitialized variables. (If building for linux, Valgrind is supposed
> to be able to do this at a much reduced price.)
MSVC++ has this covered, so I'm all set.
> (4) Run your program
> thru static code checkers, like Coverity, that'll do an amazing job of
> finding codepaths under which local variables aren't initialized. (Far
> better than warning level 4).
I've heard of these. Amazing programs... Have you had experience
with them?
> Short summary of things: there's a few ways you can try and find
> problems like the above for free, and several ways you can spend $$$
> and find them faster. As lots of people have mentioned, your
> insistence on shaving a few ms in release builds is probably hurting
> you. You can very easily write some code that does a bunch of checks
> in debug, and goes away in release builds.
There's a misunderstanding that I'm producing bad code to shave off ms
in Release builds. I know of, and employ, many Debug build checks...
assert() calls being the most numerous, but I even have some of my own
in-depth methods for catching even more things. Trust me, I am not
trying to save ms of time. I am trying to produce robust code. The
fact that I lose the default constructor, and thus lose the compiler's
warning of using a variable before being initialized (both at compile
time and at run time), is not a good thing. This allows bugs to creep
in. This is what my post is all about.
Luckily, in the new C++ standard, my desire of keeping the default
constructor is something I can do. I hope the MSVC++ compiler
implements this feature soon. :)
Thanks for your time, Nathan.
Jason
Since this is usually stack or heap memory, why would INT 3 be
significant? It's not likely to be executed. Deleted memory is set to
0xdd in memory and I've also seen 0xcd used for other states. Here's a
link:
http://www.docsultant.com/site2/articles%5Cdebug_codes.html
--
Paul
This is what I am doing. (I either have to do this, or make my color
struct an aggregate again by removing all my convenient non-default
constructors that I made.)
Why is this heavier? In the final compilation, shouldn't this produce
zero CPU instructions?
> This is the most lightweight solution you can do in C++ if you already
> have another constructor. Of course the compiler might add some implicit
> function calls to it. However, if your structure only contains POD
> types, they will remain uninitialized. There's a good chance the
> compiler can inline this for you, and remove the empty body.
All defined functions inside of a class body are inlined by default,
even without the "inline" specifier. (The C++ standard states so,
AFAIK, it's not just a MSVC++ addition.)
> Another solution to consider: simply don't write your own constructor at
> all. Create a static function, or a non-member function to initialize
> your data. Example:
>
> struct Color
> {
> int r, g, b;
> static Color Create(int ar, int ag, int ab)
> {
> Color c;
> c.r = ar;
> c.g = ag;
> c.b = ab;
> return c;
> }
> };
>
> This has to perform two assignment operations for each member, though.
Nice tip. Another downfall is that it wouldn't be able to be used in
initializing an array of Color, since you'd have to manually call this
for each element, rather than calling the constructor right in the
array declaration.
> If performance is really so important for you, you might want to use a
> less convenient method:
>
> inline void InitColor(Color& c, int ar, int ag, int ab)
> {
> c.r = ar;
> c.g = ag;
> c.b = ab;
> }
>
> The compiler will likely inline this for you in release builds. And
> there's always a possibility of using a macro (ouch!).
Ha ha :) Performance isn't that important. I just want robust code,
and want my compiler's warnings to be intact.
> Otherwise my tip would be to avoid early optimizations, and optimize
> only after doing some benchmarking. Did you know that calling malloc
> just once can be a thousand times slower than initializing those
> integers with 0? There is a possibility that you're trying to optimize
> something that's not likely to be anywhere near the bottleneck.
I agree 100%. Again, I am not concerned with optimization at this
time. I am concerned only with robust code. (I wish I had never
mentioned making my own constructor initialize the fields to 0 would
have the downfall of being slow.) My concern is simply that I want
the compiler's warnings for when I use "Color" before initializing
it. With its default constructor, I get those warnings. With my
default constructor, I don't (since it assumes I'm actually
initializing it, of course.)
Thanks for your time, Tom
Jason
Because I think I've heard it a million times, and it's sunken in,
even though I have direct evidence of my MSVC++ compiler filling all
uninitialized values with C's. Silly me. So, MSVC++ does just what I
want, except it doesn't randomize them, but it doesn't need to, since
the run-time checker will stop the program anyway, so any inadvertent
access will be caught. And by having all C's, instead of randomness,
you can easily detect these variables in the Watch window.
So, all's well!
> "WFC Technical Note 006 - Funny Memory Values"http://www.samblackburn.com/wfc/technotes/WTN006.htm
Yup! :)
Thanks for your time, Alex
Jason
Very true! I remember hearing about this some time ago...
Thanks for all your replies, Ben Voigt!
Jason
Beyond reason? All memory is either read first or written to
first. If you want to prove write happened first, you've got
to do just that. Consider this (admidittedly pathological) case:
void foo(int& v)
{
if(rand() & 0x01)
v = 0;
}
int main(int argc, char** argv)
{
srand(time(NULL));
int v;
foo(v);
printf("v = %d", v);
return 0;
}
Sometimes, things are initialized, sometimes not. I just compiled
this with DevStudio 2005SP1, debug win32 console app. Guess whether
warning level 4 catches this - Nope. I went to C/C++ properties,
selected basic runtime checks, and set to both (stack frames +
uninitialized varbs). Then I ran it. Sometimes, I get v=0, sometimes
v=-858993460 (0xCCCCCCCC). Did I get a single notice that it was used
uninitialized? Nope. Are you so sure these DevStudio "checks" are
worth the electrons they printed on?
>> (3) Pay $$$ for a program like Boundschecker that'll make a
>> super-special debug build that does runtime checks for
>> uninitialized variables. (If building for linux, Valgrind is
>> supposed to be able to do this at a much reduced price.)
>MSVC++ has this covered, so I'm all set.
If it did cover this, I wouldn't have mentioned it. MSVC++ doesn't
trap uninitialized variables in allocated memory, only locals on the
stack. Boundschecker/Valgrind will do this. They'll catch the error in
my pathological testcase above. (Well, assuming that you win the
rand() lottery.) Are you programming defensively enough yet? Don't
have any rose-colored glasses about what MSVC++ covers, until you've
proven it works.
>> (4) Run your program thru static code checkers, like Coverity,
>> that'll do an amazing job of finding codepaths under which local
>> variables aren't initialized. (Far better than warning level 4).
>I've heard of these. Amazing programs... Have you had experience
>with them?
We ran a large (200,000+ lines of code) app written at work.
Coverity found a *lot* of things that were sketchy at best, and
downright unsafe at worst. All the code had been compiling at warning
level 4 for years. (Minus a few ignorable ones like identifier
truncated to 255 chars, etc). I doubt it's cheap, though, but I wasn't
involved in that process -- just the job of fixing what it did find.
I believe it is beyond reason to have a 'native' data type checked on
each access at run-time -- which is what I *thought* you meant.
Obviously, I misunderstood you.
> All memory is either read first or written to
> first. If you want to prove write happened first, you've got
> to do just that. Consider this (admittedly pathological) case:
>
> [ ...... snipped good example ...... ]
>
> Sometimes, things are initialized, sometimes not. I just compiled
> this with DevStudio 2005SP1, debug win32 console app. Guess whether
> warning level 4 catches this - Nope. I went to C/C++ properties,
> selected basic runtime checks, and set to both (stack frames +
> uninitialized varbs). Then I ran it. Sometimes, I get v=0, sometimes
> v=-858993460 (0xCCCCCCCC). Did I get a single notice that it was used
> uninitialized? Nope. Are you so sure these DevStudio "checks" are
> worth the electrons they printed on?
I know MSVC++ won't catch everything. I *did* think it would catch
any uninitialized variable at run-time, though. Thanks for showing me
that it doesn't.
In defense of MSVC++, it is worth the electrons. Consider that the
above example doesn't occur often. Much more often, simpler bugs
occur that it catches, and it tells me at compile time or at run-time,
instead of allowing the bug to creep around for days. This saves me
time. So, *anything* it catches is a time saver, even if it's just
telling me I'm being stupid by using = instead of ==.
I don't rely on MSVC++ to act as my debugger. I have many tools made
myself, such as an entire memory checker, that does things like bounds
checking and garbage shredding, to make sure my program crashes as
quickly as possible when things go bad, to catch the bug more quickly
and easily.
> >MSVC++ has this covered, so I'm all set.
>
> If it did cover this, I wouldn't have mentioned it.
I'm sorry, I didn't realize it wouldn't catch the above case that you
showed. Thanks for pointing it out.
> Are you programming defensively enough yet?
I try my best; I make my own tools to cover MSVC++'s deficiencies.
The whole point of this thread is my reaction to C++ stripping away
something that allows me to program defensively.
> Don't
> have any rose-colored glasses about what MSVC++ covers, until you've
> proven it works.
I don't. I was just unaware that run-time checks of non-initialized
was limited to locals on the stack. My bad. Again, I know that MSVC+
+ doesn't catch a lot of things, because I've seen software that can
catch things I wouldn't have dreamed any compiler would ever catch...
it's amazing stuff.
> We ran a large (200,000+ lines of code) app written at work.
> Coverity found a *lot* of things that were sketchy at best, and
> downright unsafe at worst. All the code had been compiling at warning
> level 4 for years. (Minus a few ignorable ones like identifier
> truncated to 255 chars, etc). I doubt it's cheap, though, but I wasn't
> involved in that process -- just the job of fixing what it did find.
My code base is getting near there. I don't have the resources to pay
what they likely want, considering they hide their prices behind a
phone number. I try to make up for it by running vigorous nightly
debug tests that try and squeeze out all bugs, and by running per-
execution unit tests that ensure everything is running as it should be
each and every time I launch the application.
Thanks, Nathan!
Jason