Class
. Variable and function names start with a lower
case letter.
Multiword classes and variables have words after the first
capitalized, such as the class RecursiveSlotValue
or the
variable currentItem
. Constants have names that are all
upper case and separated by underscores, such as WHEEL_DOWN
.
Member variables have names starting with m_
, such as
m_data
Static variables have names starting with
s_
.
foo.h
, one typically has:
and at the end one has:#ifndef __Foo_h__ #define __Foo_h__
(Incidentally, the common practice of having a comment like#endif
/* __foo_h__ */
after the #endif
is a
deviation from the C++ standard, and is not used in
Fungimol.) These internal include guards cause redundant inclusions
of foo.h
to have no effect, which is usually what you
want. In a large program it is difficult and not worth the effort to
avoid such redundant inclusions.
However, if you only have internal include guards, then the C
preprocessor will have to parse the entirety of each redundant
inclusion of the header file. If we also have the include guards
outside the inclusion of foo.h
, like this:
then the C preprocessor will not have to parse foo.h when it is included after the first time. It is easy with Gnu Emacs macros to automate the addition of the external include guards.#ifndef __Foo_h__ #include "Foo.h" #endif
The preprocessor symbol used for the include guard starts with two
underscores, then has the filename with "." and "/" transliterated to
underscores, then ends with two underscores. We use the same
convention whether the include file is included with something like
#include "Foo.h"
or something like
#include <Foo.h>
.
For system header files, I can't control the name of the internal include guard that is used (and there may not be one), so we do something like this:
The symbol is defined after the file is included just in case the system header file happens to have the same include guard name as the external include guard I'm using.#ifndef __iostream_h__ #include <iostream.h> #define __iostream_h__ #endif
Foo
is normally declared in a file
Foo.h
and defined in a file Foo.cpp
. It is
quite common to know the name of a class and to want to look at the
definition or declaration, so this is good.
Foo.h
with this text:
and the class#ifndef __Foo_h__ #define __Foo_h__ #ifndef __Quaternion_h__ #include "Quaternion.h" #endif class String; class Foo { Quaternion m_q; public: String foo (const Quaternion &q); } #endif
Foo
would be defined as follows in
Foo.cpp
:
Since#include "Foo.h" #ifndef __Quaternion_h__ #include "Quaternion.h" #endif #ifndef __String_h__ #include "String.h" #endif String Foo::foo (const Quaternion &q) { m_q = q; return "Hi"; }
Foo.cpp
includes Foo.h
as the
first line, the compiler is checking that Foo.h
includes
everything it needs to compile. Since the size of the class
String
does not need to be known to lay out an instance
of Foo
, we don't need to include String.h
from Foo.h
. This way, a class that uses Foo
but does not use the method Foo::foo
may not include
String.h
, so it may not have to
be recompiled if a comment is changed in String.h
.
NEW
instead of new
. The macro
NEW
is defined in Util/MemoryUtil.h
. The
leak detection slows down memory allocation, but memory allocation is
slow enough already that I had to take care not to do any in the inner
loops, so I leave the leak detection turned on in the non-debug build.
If you want to allocate a new reference to a multi-argument template,
then there's a battle between C++ and the preprocessor which is
resolved in the case of two template arguments by using the macro
NEW2
. For example, in the normal case one invokes
NEW
like this to make a pointer to a string:
If we naively replace the typeNEW (String ())
String
with the template class
HashTable <String, FactoryList>
, we get an error
when attempting to compile the following:
The problem is that so far as the preprocessor is concerned,NEW (HashTable <String, FactoryList> ()) // Error
NEW
is now taking two arguments, specifically
HashTable <String
and FactoryList> ()
.
NEW
is only defined to take one argument, so this does not work.
The problem is solved by using the macro NEW2
which takes two
arguments and does the right thing with them:
If you needNEW2 (HashTable <String, FactoryList> ())
NEW3
, add it to Util/MemoryUtil.h
.
If you have some code that puts a pointer into static storage, and you
don't want the leak detector to complain about it, you'll have to
register a deallocator with the leak detector. Read
Util/MemoryUtil.h
for instructions.
After allocation but before use, the allocated memory is filled with
many copies of the easily recognizable 32-bit hexadecimal constant
0xbabeface
. After being freed, the previous contents of
the allocated memory is overwritten with the constant
0xdeadbeef
. Some out-of-bounds array references are
detected by verifying that the program does not disturb the constant
0xfeeddeed
that is placed before the memory block or the
constant 0xfadedebb
that is placed after the memory
block. If you set the radix to 16, you will occasionally see these
constants with the debugger.
This does not help when C library code is called, since C code does
not call new
and delete
.
The mcheck
package is used under Linux to
indulge my paranoia in this case.
Foo
defined
above would have this in Foo.h
:
and this in#ifndef __Foo_h__ #define __Foo_h__ class Quaternion; class String; class Foo { struct FooData; FooData *m_data; public: Foo (); ~Foo (); String foo (const Quaternion &q); } #endif
Foo.cpp
:
There are several things going on here:#include "Foo.h" #ifndef __Quaternion_h__ #include "Quaternion.h" #endif #ifndef __String_h__ #include "String.h" #endif #ifndef __MemoryUtil_h__ #include "MemoryUtil.h" #endif #ifndef __myassert_h__ #include "myassert.h" #endif struct Foo::FooData { Quaternion m_q; }; Foo::Foo () : m_data (NEW (FooData ())) {} Foo::~Foo () { assert (m_data); delete m_data; m_data = 0; } String Foo::foo (const Quaternion &q) { m_data->m_q = q; return "Hi"; }
Foo.h
no longer needs to include any
other header files.
Foo
now needs a non-default constructor and
destructor to allocate and free the pointer to the implementation.
Foo::FooData
was named
FooData
instead of just Data
because of a
gdb
bug. If there are classes Foo::Data
and
Bar::Data
, then the debugger only remembers that there
was a class named Data
, so does not correctly determine
the layout of one or the other. If we give them names like
Foo::FooData
and Bar::BarData
instead, then
the debugger is able to correctly display the data.
Foo::FooData
structure.
This way they can be freely modified without causing users of
Foo
to be recompiled. Note that these member functions
would avoid the performance penalty from the insulation.
and not like this:class SceneGraphConfiguration : public Configuration // Good { ... };
or like this:class SceneGraphConfiguration : public Configuration // Bad { ... };
class SceneGraphConfiguration : public Configuration { // Bad ... };
SP
, which stands for "Smart Pointer". This has
methods defined on it to make it behave like a pointer, except it
maintains the reference count of the object it is pointing to, and it
takes care of destructing the recently-pointed-to object if the
reference count is zero after being decremented. The class that
defines the reference count is called Refcount
, and it is
a subclass of most classes in the program.
Here's a useful idiom: if c
has the type, say,
SP<Configuration>
, then the expression
&*c
has the type Configuration *
. Since
pointers get more implicit conversions than smart pointers,
ordinary pointers are more useful for some purposes.
Refcount
passes this
to some other function,
there is the possibility that the other function will convert
this
to a smart pointer, which will in turn increment and
then decrement the reference count and then deallocate the object
at a time when the constructor or destructor intended to do more work
with it. To avoid this, the constructor or destructor
should call ref()
before passing this
to the
function and
call deref()
afterward. This way, the reference count
will not be decremented to zero. deref()
does not
deallocate the object if it decreases the reference count to zero.
An exception to this rule is a function that stores a reference to the object. Suppose the function looks like this:
BecauseSP<Bar> theGlobalBar = 0; void foo (Bar *b) { doSomething (b); theGlobalBar = b; // Error: theGlobalBar may point to deallocated memory }
foo
stores a reference, it might be called as
foo (NEW (Bar ()))
. In this case, if
doSomething
does smart pointer manipulation on its argument
b
, it will deallocate the object when it is done with the
smart pointer manipulation, so theGlobalBar
will be left
with a pointer to deallocated storage, unless the program crashes
while trying to update b's reference count while assigning to
theGlobalBar. This can easily be fixed by
writing foo
like this:
IfSP<Bar> theGlobalBar = 0; void foo (Bar *b) { SP<myB> = b; doSomething (myB); theGlobalBar = myB; // OK }
foo
didn't store a reference to b
then
a call like foo (NEW (Bar ()))
is
guaranteed to be a memory leak. Since we aggressively track down
memory leaks, transforming
this memory leak bug into a program crash just causes the bug to be
found sooner rather than later, which is no great loss. Therefore a
subroutine that does not store an argument need not take the argument
as a smart pointer.
Functions that return a pointer to a reference counted object should generally return it as a smart pointer. In code like
one might be concerned about the following scenario:SP<Bar> baz (); ... foo (baz ()) ... // OK
baz
is converted to a pointer.
foo
.
foo
.
foo
is called with a possibly deallocated object.
foo
is called. This rule from the standard does
not save the similar code
which is bad because the caller is using x to store a pointer to a reference-counted object without using a smart pointer. The fix is to declare x as a smart pointer:SP<Bar> baz (); Bar* x = baz (); ... foo (x) ... // Error
SP<Bar> baz (); SP<Bar> x = baz (); ... foo (x) ... // OK
Objects with reference counts must not be allocated on the stack,
since they are likely to then be freed, thus corrupting the stack.
For instance, if
Bar
is a subclass of Refcount
, the following code is
likely to fail:
A reasonable fix would be to changeBar b; doSomething (&b); // Bad: doSomething may free b
b
to a smart pointer:
SP<Bar> b = NEW (Bar ()); doSomething (b);
Dynavec
. This defines reasonably efficent
variable-length arrays. A debug build will do bounds checking on
array references.