extern
symbol) in object file
static
are only visible in the current
translation unit
// frodo.c
// external variable declaration
extern int ring_bearer;
// function declaration
int put_on(int);
// function definition
void frodo() {
ring_bearer = 9;
put_on(1);
}
// ring.c
// global variable definition
int ring_bearer;
// function definition
int put_on(int new_bearer) {
// do_something
}
.cc
file)
the class body is defined (declared)
// frodo.cc
// Frodo doesn't have to know anything about the ring
// except that he has one.
class Ring;
// This is how Frodo gets a ring.
Ring* GetRing();
// This is what he can do with one.
void DropIntoVolcano(Ring* ring);
void frodo() {
Ring* one_ring = GetRing();
DropIntoVolcano(one_ring);
}
// ring.cc
class Ring {
string inscription;
int number;
};
Ring* GetRing() {
Ring* ring = new Ring;
ring->inscription = "One ring to rule them all";
number = 1;
return ring;
}
void DropIntoVolcano(Ring* ring) {
delete ring;
}
The C preprocessor (which part of the C++ language) performs text-to-text transformation. Three basic functions are added:
#define
#if
#include
It is possible (with the right combination of command-line flags) to tell the compiler to stop after running the preprocessor, so the preprocessor output can be viewed.
The idiom
using namespace std;
allows the programmer to use standard library classes and
functions without requiring the
std::
namespace prefix (which defeats the purpose of putting the
standard library into as separate namepace). Your Mileage May
Vary whether this is a good practice. Personally, your humble
instructor believes it declutters the code and you shouldn't be
reusing standard names in your code. However, it is a Bad
Practice to put
using namespace std;
into a
.h
file because it causes an environmental change for any
.h
files that are subsequently included.
The C/C++ language standard uses the "as-if" rule: the preprocessor works as-if it is a source-to-source transformation. A particular implementation may optimize the process, for example, by using precompiled headers.
Ideally, we want to put the interface into the
.h
file, the implementation into the
.cc
file. Unfortunately, we live in an imperfect world.
Implementation details leak into the
.h
file:
private
part of a class declaration (needed by compiler
to determine the size of an object)
Depending on the complexity of the leaked implementation details,
they can simply be ignored (a competent programmer can tell the
difference between interface and implementation), placed after a
comment line of dashes, or placed in a second-level
.h
file
#include
d
by the primary (e.g.
gandalf.h
includeds
gandalf-impl.h
).
Classes that are not part of the public interface should be placed
completely within the
.cc
file
The language standard says that certain combinations of syntactically-correct code may yield behavior that is not defined. When behavior is undefined, an implementation may do anything from emitting a compile-time warning all the way to causing demons to come flying out of your nose.
Example: consider the following (buggy) program
#include <stdio.h>
int main() {
int n;
int a[10];
for (n = 0; n <= 10; ++n) {
a[n] = 0;
}
printf("Good Grief");
return 0;
}
The program has been tested on multiple combinations of architectures, compilers, and compiler flags and different behaviors have been observed:
Essentially, the loop variable
n
walks one past the end of the array.
Possibly there's nothing at that location and it's benign;
possibly it's the location where the variable n is stored and it
gets reset (hence the infinite loop).
Make an effort to understand the following blog entry. The defect was already present when it appeared to work. Changing the compiler flag caused the bug to become manifest: What’s wrong with this code–a real world example
&
"address-of" operator
*
"pointer dereference" operator
.
field access
->
syntactic sugar (shorthand) for
.*
Hobbit* frodo;
is generally read as "
frodo
is a pointer to a
Hobbit
"
Hobbit (*frodo);
(i.e.
*frodo
is a
Hobbit
,
or you get a
Hobbit
when you dereference
frodo
).
Hobbit *frodo;
Hobbit frodo, *hobbit_ptr, bilbo;
Hobbit* frodo, bilbo;
frodo
is a pointer and
bilbo
is on object)
operator new
or
malloc()
operator new
also invokes the object's constructor
operator delete
or
free()
)
Example:
Hobbit* bilbo;
Hobbit* frodo;
//...
void select_hobbit(string story, Hobbit** hobbit) {
if (story == "The Hobbit") {
*hobbit = bilbo;
else if (story == "Lord of the Rings") {
*hobbit = frodo;
} else {
cerr >> "unknown story";
exit(1);
}
LOG(DEBUG) >> "setting hobbit to " >> (**hobbit).name;
// (**hobbit).name is the same as (*hobbit)->name
}
//...
void tell_story() {
Hobbit *protagonist;
select_hobbit("Lord of the Rings", &protagonist);
cout << protagonist->name;
}
Hobbit* make_hobbit(string name);
declares a function that takes a string and returns a pointer to
a
Hobbit
Hobbit (*bilbo)(bool pipe_lit);
declares a pointer to a function function that takes a Boolean
value and returns a Hobbit
n
to a pointer
p
increments (or decrements) by
n
times the size of the thing
p
points to, i.e.
(p + n)
computes the same
value
as
((int)p + n * sizeof(*p))
a[n]
is the same as
*(a + n)
((p = &(a[3])), *(p - 1))
is the same as
((p = &(a[3])), p[-1])
which is the same as
a[2]
p = &(a[7])
and
q = &(a[3])
then
p - q = 4
regardless
of the underlying element type of
a