We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.
All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.
In this chapter the usage of C++ is further explored. The possibility to
declare functions in structs is further illustrated using examples. The
concept of a class is introduced.
:: is described first. This operator can be
used in situations where a global variable exists with the same name as a
local variable:
#include <stdio.h>
int
counter = 50; // global variable
int main()
{
for (register int counter = 1; // this refers to the
counter < 10; // local variable
counter++)
{
printf("%d\n",
::counter // global variable
/ // divided by
counter); // local variable
}
return (0);
}
In this code fragment the scope operator is used to address a global
variable instead of the local variable with the same name. The scope operator
is more extensively used than shown here, but its main purpose will be
described later (in chapter 6).
<<, called
the
insertion operator and >>, called the
extraction operator. This is illustrated in the example below:
#include <iostream>
int main()
{
int
ival;
char
sval[30];
cout << "Enter a number:" << endl;
cin >> ival;
cout << "And now a string:" << endl;
cin >> sval;
cout << "The number is: " << ival << endl
<< "And the string is: " << sval << endl;
}
This program reads a number and a string from the cin stream (usually
the keyboard) and prints these data to cout. Concerning the streams and
their usage we note:
iostream. In the
examples in the Annotations this header file is often not mentioned
explicitly. Nonetheless, it must be included (either directly or
indirectly) when these streams are used. Comparable to the use of the using
namespace std; clause, the reader is expected to #include <iostream> with
all the examples unless otherwise stated.
cout, cin and cerr are in fact `objects' of a
given class (more on classes later), processing the input and output of a
program. Note that the term `object', as used here, means the set of data and
functions which defines the item in question.
cin reads data and copies the information to variables
(e.g., ival in the above example) using the extraction operator >>. We
will describe later how operators in C++ can perform quite different
actions than what they are defined to do by the language grammar, such as is
the case here. We've seen function overloading. In C++ operators can
also have multiple definitions, which is called operator overloading.
cin, cout and cerr (i.e.,
>> and <<) also manipulate variables of different types. In the above
example cout << ival results in the printing of an integer value, whereas
cout << "Enter a number" results in the printing of a string. The actions
of the operators therefore depend on the type of supplied variables.
cout is realized by inserting the
endl symbol, rather than using the string "\n".
cin, cout and cerr are in fact not part of the
C++ grammar, as defined in the compiler which parses source files. The
streams are part of the definitions in the header file iostream. This is
comparable to the fact that functions as printf() are not part of the
C grammar, but were originally written by people who considered such
functions handy and collected them in a run-time library.
Whether a program uses the old-style functions like printf() and
scanf() or whether it employs the new-style streams is a matter of taste.
Both styles can even be mixed. A number of advantages and disadvantages is
given below:
C functions printf() and
scanf(), the usage of the insertion and extraction operators is more
type-safe. The format strings which are used with printf() and
scanf() can define wrong format specifiers for their arguments, for which
the compiler sometimes can't warn. In contrast, argument checking with
cin, cout and cerr is performed by the compiler. Consequently it
isn't possible to err by providing an int argument in places where,
according to the format string, a string argument should appear.
printf() and scanf(), and other functions which
use format strings, in fact implement a mini-language which is interpreted at
run-time. In contrast, the C++ compiler knows exactly which in- or
output action to perform given which argument.
C++. Again, it
requires a little getting used to, ascending from C, but after that these
overloaded operators feel rather comfortably.
Iostreams are extensible: functionality can easily be added
to the existing functionality, a phenomenon called
inheritance. Inheritance is discussed in detail in chapter
13.
cin, cout
and cerr. In chapter 5 iostreams will be covered in
greater detail. Even though
printf() and friends can still be used in
C++ programs, iostreams are practically replacing the old-style C
I/O functions like printf(). If you think you still need to use
printf() and related functions, think again: in that case you've probably
not yet completely understood the possibilities of iostream objects.
const very often occurs in C++ programs, although const
is also part of the C grammar, in C const is much less often used.
The const keyword is a modifier which states that the value of a variable
or of an argument may not be modified. In the below example an attempt is made
to change the value of a variable ival, which is not legal:
int main()
{
int const // a constant int..
ival = 3; // initialized to 3
ival = 4; // assignment leads
// to an error message
return 0;
}
This example shows how ival may be initialized to a given value in its
definition; attempts to change the value later (in an assignment) are not
permitted.
Variables which are declared const can, in contrast to C, be used as
the specification of the size of an array, as in the following example:
int const
size = 20;
char
buf[size]; // 20 chars big
Another usage of the keyword const is seen in the declaration of
pointers, e.g., in pointer-arguments. In the declaration
char const *buf;
buf is a pointer variable, which points to chars. Whatever is
pointed to by buf may not be changed: the chars are declared as
const. The pointer buf itself however may be changed. A statement as
*buf = 'a'; is therefore not allowed, while buf++ is.
In the declaration
char *const buf;
buf itself is a const pointer which may not be changed. Whatever
chars are pointed to by buf may be changed at will.
Finally, the declaration
char const *const buf;
is also possible; here, neither the pointer nor what it points to may be
changed.
The
rule of thumb for the placement of the keyword const is the
following: whatever occurs to the left to the keyword may not be changed.
Although simple, this rule of thumb is not often used. For example, Bjarne Stroustrup states (in http://www.research.att.com/~bs/bs_faq2.html#constplacement):
Should I put "const" before or after the type?Below we'll see an example where applying this simple `before' placement rule for the keywordI put it before, but that's a matter of taste. "const T" and "T const" were always (both) allowed and equivalent. For example:
const int a = 1; // ok int const b = 2; // also okMy guess is that using the first version will confuse fewer programmers (``is more idiomatic'').
const produces unexpected (i.e., unwanted)
results. Apart from that, the `idiomatic' before-placement conflicts with the
notion of
const functions, which we will encounter in section
6.2, where the keyword const is also written behind the
name of the function.
The definition or declaration in which const is used should be read from
the variable or function identifier back to the type indentifier:
``Buf is a const pointer to const characters''This rule of thumb is especially handy in cases where confusion may occur. In examples of C++ code, one often encounters the reverse:
const preceding what should not be altered. That this may result in
sloppy code is indicated by our second example above:
char const *buf;
What must remain constant here? According to the sloppy interpretation,
the pointer cannot be altered (since const precedes the pointer-*). In
fact, the charvalues are the constant entities here, as will be clear when it
is tried to compile the following program:
int main()
{
char const *buf = "hello";
buf++; // accepted by the compiler
*buf = 'u'; // rejected by the compiler
return 0;
}
Compilation fails on the statement *buf = 'u';, not on the
statement buf++.
Marshall Cline C++ FAQ gives the same rule (paragraph 18.5) , in a similar context:
[18.5] What's the difference between "const Fred* p", "Fred* const p" and "const Fred* const p"?You have to read pointer declarations right-to-left.
int
int_value;
int
&ref = int_value;
In the above example a variable int_value is defined. Subsequently a
reference ref is defined, which due to its initialization addresses the
same memory location which int_value occupies. In the definition of
ref, the reference operator & indicates that ref is not
itself an integer but a reference to one. The two statements
int_value++; // alternative 1
ref++; // alternative 2
have the same effect, as expected. At some memory location an int
value is increased by one --- whether that location is called int_value or
ref does not matter.
References serve an important function in C++ as a means to pass
arguments which can be modified. E.g., in standard C, a function which
increases the value of its argument by five but which returns nothing
(void), needs a pointer parameter:
void increase(int *valp) // expects a pointer
{ // to an int
*valp += 5;
}
int main()
{
int
x;
increase(&x) // the address of x is
return 0; // passed as argument
}
This construction can also be used in C++ but the same effect
can be achieved using a reference:
void increase(int &valr) // expects a reference
{ // to an int
valr += 5;
}
int main()
{
int
x;
increase(x); // a reference to x is
return 0; // passed as argument
}
Actually, references are implemented using pointers. So, references in
C++ are just pointers, as far as the compiler is concerned. However, the
programmer does not need to know or to bother about levels of
indirection.
It can be argued whether code such as the above is clear: the statement
increase (x) in the main() function suggests that not x
itself but a copy is passed. Yet the value of x changes because of
the way increase() is defined.
Our suggestions for the usage of references as arguments to functions are therefore the following:
void some_func(int val)
{
cout << val << endl;
}
int main()
{
int
x;
some_func(x); // a copy is passed, so
return 0; // x won't be changed
}
void by_pointer(int *valp)
{
*valp += 5;
}
void by_reference(int &valr)
{
valr += 5;
}
int main ()
{
int
x;
by_pointer(&x); // a pointer is passed
by_reference(x); // x is altered by reference
return 0; // x might be changed
}
struct, is passed as argument, or is returned from the function.
In these cases the copying operations tend to become
significant factors when the entire structure must be copied, and it is
preferred to use references. If the argument isn't changed by the
function, or if the caller shouldn't change the returned information,
the use of the const keyword is appropriate and should be used.
Consider the following example:
struct Person // some large structure
{
char
name [80],
address [90];
double
salary;
};
Person
person[50]; // database of persons
// printperson expects a
void printperson (Person const &p)
{ // reference to a structure
// but won't change it
cout << "Name: " << p.name << endl <<
"Address: " << p.address << endl;
}
// get a person by indexvalue
Person const &getperson(int index)
{
return person[index]; // a reference is returned,
} // not a copy of person[index]
int main ()
{
Person
boss;
printperson (boss); // no pointer is passed,
// so variable won't be
// altered by the function
printperson(getperson(5));
// references, not copies
// are passed here
return 0;
}
int &func()
{
static int
value;
return value;
}
This allows the following constructions:
func() = 20;
func() += func();
It is probably superfluous to note that such constructions should not
normally be used. Nonetheless, there are situations where it is useful to
return a reference. Even though this is discussed more extensively only later,
we have seen an example of this phenomenon at our previous discussion of the
iostreams. In a statement like cout << "Hello" << endl;, the insertion
operator returns a reference to cout. So, in this statement first the
"Hello" is inserted into cout, producing a reference to cout. Via
this reference the endl is then inserted in the cout object, again
producing a reference to cout. This latter reference is not further used.
A number of differences between pointers and references is pointed out in the list below:
int &ref; ref refer to?
external. These
references were initialized elsewhere.
& is used with a reference, the
expression yields the address of the variable to which the reference
applies. In contrast, ordinary pointers are variables themselves, so the
address of a pointer variable has nothing to do with the address of the
variable pointed to.
structs (see
section 2.5.14). Such functions are called
member functions or
methods.
This section discusses the actual definition of such functions.
The code fragment below illustrates a struct in which data fields for a
name and address are present. A function print() is included in the
struct definition:
struct person
{
char
name [80],
address [80];
void
print (void);
};
The member function print() is defined using the structure name
(person) and the scope resolution operator (::):
void person::print()
{
cout << "Name: " << name << endl
"Address: " << address<< endl;
}
In the definition of this member function, the function name is preceded
by the struct name followed by ::. The code of the function shows how
the fields of the struct can be addressed without using the type name: in
this example the function print() prints a variable name. Since
print() is a part of the struct person, the variable name
implicitly refers to the same type.
This struct could be used as follows:
person
p;
strcpy(p.name, "Karel");
strcpy(p.address, "Rietveldlaan 37");
p.print();
The advantage of member functions lies in the fact that the called
function can automatically address the data fields of the structure for which
it was invoked. As such, in the statement p.print() the structure p is
the `substrate': the variables name and address which are used in the
code of print() refer to the same struct p.
void, char, short,
int, long, float and double. C++ extends these five basic types with
several extra types: the types bool, wchar_t and long double. The type
long double is merely a double-long double datatype. Apart from these
basic types a standard type string is available. The datatypes bool,
and wchar_t are covered in the following sections, the datatype string
is covered in chapter 4.
void, char, int,
float and double. C++ extends these five basic types with several
extra types. In this section the type
bool is introduced.
The type bool represents boolean (logical) values, for which the (now
reserved) values
true and
false may be used. Apart from these reserved
values, integral values may also be assigned to variables of type bool,
which are implicitly converted to true and false according to the
following
conversion rules (assume intValue is an int-variable, and
boolValue is a bool-variable):
// from int to bool:
boolValue = intValue ? true : false;
// from bool to int:
intValue = boolValue ? 1 : 0;
Furthermore, when bool values are inserted into, e.g., cout, then
1 is written for true values, and 0 is written for false
values. Consider the following example:
cout << "A true value: " << true << endl
<< "A false value: " << false << endl;
The bool data type is found in other programming languages as
well. Pascal has its type Boolean, and Java has a boolean
type. Different from these languages, C++'s type bool acts like a kind
of int type: it's primarily a documentation-improving type, having just
two values true and false. Actually, these values can be interpreted
as enum values for 1 and 0. Doing so would neglect the philosophy
behind the bool data type, but nevertheless: assigning true to an
int variable neither produces warnings nor errors.
Using the bool-type is generally more intuitively clear than using
int. Consider the following prototypes:
bool exists(char const *fileName); // (1)
int exists(char const *fileName); // (2)
For the first prototype (1), most people will expect the function to
return true if the given filename is the name of an existing
file. However, using the second prototype some ambiguity arises: intuitively
the return value 1 is appealing, as it leads to constructions like
if (exists("myfile"))
cout << "myfile exists";
On the other hand, many functions (like
access(),
stat(), etc.)
return 0 to indicate a successful operation, reserving other values to
indicate various types of errors.
As a rule of thumb we suggest the following: If a function should
inform its caller about the success or failure of its task, let the function
return a bool value. If the function should return success or various
types of errors, let the function return enum values, documenting the
situation when the function returns. Only when the function returns a
meaningful integral value (like the sum of two int values), let the
function return an int value.
wchar_t type is an extension of the char basic type, to accomodate
wide character values, such as the
Unicode character set.
Sizeof(wchar_t) is 2, allowing for 65,536 different character values.
Note that a programming language like Java has a data type char that
is comparable to C++'s wchar_t type, while Java's byte data
type is comparable to C++'s char type. Very convenient....
C++ has three special keywords which are concerned with data hiding:
private,
protected and
public. These keywords can be inserted in
the definition of a struct. The keyword public defines all subsequent
fields of a structure as accessible by all code; the keyword private
defines all subsequent fields as only accessible by the code which is part of
the struct (i.e., only accessible for the member functions). The keyword
protected is discussed in chapter 13, and is, for the time
being, beyond the scope of the ongoing discussion.
In a struct all fields are public, unless explicitly stated otherwise.
Using this knowledge we can expand the struct person:
struct person
{
public:
void
setname (char const *n),
setaddress (char const *a),
print (void);
char const
*getname (void),
*getaddress (void);
private:
char
name [80],
address [80];
};
The data fields name and address are only accessible for the
member functions which are defined in the struct: these are the functions
setname(), setaddress() etc.. This property of the data type is given
by the fact that the fields name and address are preceded by the
keyword private. As an illustration consider the following code fragment:
person
x;
x.setname ("Frank"); // ok, setname() is public
strcpy (x.name, "Knarf"); // error, name is private
The concept of data hiding is realized here in the following manner. The
actual data of a struct person are named only in the structure
definition. The data are accessed by the outside world by special functions,
which are also part of the definition. These member functions control all
traffic between the data fields and other parts of the program and are
therefore also called `interface' functions. The data hiding which is thus
realized is illustrated further in figure 2.

Also note that the functions setname() and setaddress() are
declared as having a char const * argument. This means that the functions
will not alter the strings which are supplied as their arguments. In the same
vein, the functions getname() and getaddress() return a char const
*: the caller may not modify the strings which are pointed to by the return
values.
Two examples of member functions of the struct person are shown
below:
void person::setname(char const *n)
{
strncpy(name, n, 79);
name[79] = '\0';
}
char const *person::getname()
{
return (name);
}
In general, the power of the member functions and of the concept of data
hiding lies in the fact that the interface functions can perform special
tasks, e.g., checks for the validity of data. In the above example
setname() copies only up to 79 characters from its argument to the data
member name, thereby avoiding
array boundary overflow.
Another example of the concept of data hiding is the following. As an
alternative to member functions which keep their data in memory (as do the
above code examples), a runtime library could be developed with interface
functions which store their data on file. The conversion of a program which
stores person structures in memory to one that stores the data on disk
would mean the relinking of the program with a different library.
Though data hiding can be realized with structs, more often (almost
always) classes are used instead. A class is in principle equivalent to a
struct except that unless specified otherwise, all members (data or
functions) are private. As far as private and public are
concerned, a class is therefore the opposite of a struct. The
definition of a class person would therefore look exactly as shown
above, except for the fact that instead of the keyword struct, class
would be used. Our typographic suggestion for class names is a capital as
first character, followed by the remainder of the name in lower case (e.g.,
Person).
structs are concerned. In C it is
common to define several functions to process a struct, which then
require a pointer to the struct as one of their arguments. A fragment
of an imaginary C header file is given below:
// definition of a struct PERSON_
typedef struct
{
char
name[80],
address[80];
} PERSON_;
// some functions to manipulate PERSON_ structs
// initialize fields with a name and address
extern void initialize(PERSON_ *p, char const *nm,
char const *adr);
// print information
extern void print(PERSON_ const *p);
// etc..
In C++, the declarations of the involved functions are placed inside
the definition of the struct or class. The argument which denotes
which struct is involved is no longer needed.
class Person
{
public:
void initialize(char const *nm, char const *adr);
void print(void);
// etc..
private:
char
name[80],
address[80];
};
The struct argument is implicit in C++. A function call in C
like
PERSON_
x;
initialize(&x, "some name", "some address");
becomes in C++:
Person
x;
x.initialize("some name", "some address");
cos(), sin(), tan() etc. are to be used
accepting arguments in degrees rather than arguments in
radians. Unfortunately, the functionname cos() is already in use, and that
function accepts radians as its arguments, rather than degrees.
Problems like these are normally solved by looking for another name, e.g., the
function name cosDegrees() is defined. C++ offers an alternative
solution by allowing
namespaces to be defined: areas or regions in the
code in which identifiers are defined which cannot conflict with existing
names defined elsewhere.
Now that the
ANSI/ISO standard is implemented to a large degree in recent
compilers, the use of namespaces is more strictly enforced than in previous
versions of compilers. This has certain consequences for the setup of
class
header files. At this point in the Annotations this cannot be
discussed in detail, but in section 6.5.1 the construction of
header files using entities from namespaces is discussed.
namespace identifier
{
// declared or defined entities
// (declarative region)
}
The identifier used in the definition of a namespace is a standard C++
identifier.
Within the
declarative region, introduced in the above code
example, functions, variables, structs, classes and even (nested) namespaces
can be defined or declared. Namespaces cannot be defined within a block. So it
is not possible to define a namespace within, e.g., a function. However, it is
possible to define a namespace using multiple namespace
declarations. Namespaces are said to be open. This means that a namespace
CppAnnotations could be defined in a file file1.cc and also in a file
file2.cc. The entities defined in the CppAnnotations namespace of
files file1.cc and file2.cc are then united in one CppAnnotations
namespace region. For example:
// in file1.cc
namespace CppAnnotations
{
double cos(double argInDegrees)
{
...
}
}
// in file2.cc
namespace CppAnnotations
{
double sin(double argInDegrees)
{
...
}
}
Both sin() and cos() are now defined in the same
CppAnnotations namespace.
Namespace entities can also be defined outside of their namespaces. This topic is discussed in section 3.6.4.1.
3.6.1.1: Declaring entities in namespaces
Instead of defining entities in a namespace, entities may also be declared in a namespace. This allows us to put all the declarations of a namespace in a header file which can thereupon be included in sources in which the entities of a namespace are used. Such a header file could contain, e.g.,
namespace CppAnnotations
{
double cos(double degrees);
double sin(double degrees);
}
Namespaces can be defined without a name. Such a namespace is anonymous and it restricts the usability of the defined entities to the source file in which the anonymous namespace is defined.
The entities that are defined in the anonymous namespace are accessible the
same way as
static functions and variables in C. The static
keyword can still be used in C++, but its use is more dominant in
class definitions (see chapter 6). In situations where static
variables or functions are necessary, the use of the anonymous namespace is
preferred.
The anonymous namespace is a closed namespace: it is not possible to add entities to the same anonymous namespace using different sources.
cos() defined
in the CppAnnotations namespace the following code could be used:
// assume the CppAnnotations namespace is declared in the
// next header file:
#include <CppAnnotations>
int main()
{
cout << "The cosine of 60 degrees is: " <<
CppAnnotations::cos(60) << endl;
return 0;
}
This is a rather cumbersome way to refer to the cos() function in the
CppAnnotations namespace, especially so if the function is frequently
used.
Therefore, an abbreviated form (just cos()) can be used by
declaring that cos() will refer to CppAnnotations::cos(). For this,
the
using-declaration can be used. Following
using CppAnnotations::cos; // note: no function prototype,
// just the name of the entity
// is required.
the function cos() will refer to the cos() function in the
CppAnnotations namespace. This implies that the standard cos()
function, accepting radians, cannot be used automatically anymore. The plain
scope resolution operator can be used to reach the generic cos() function:
int main()
{
using CppAnnotations::cos;
...
cout << cos(60) // uses CppAnnotations::cos()
<< ::cos(1.5) // uses the standard cos() function
<< endl;
return 0;
}
Note that a using-declaration can be used inside a block. The
using declaration prevents the definition of entities having the same
name as the one used in the using declaration: it is not possible to
use a using declaration for a variable value in the CppAnnotations
namespace, and to define (or declare) an identically named object in the
block in which the using declaration was placed:
int main()
{
using CppAnnotations::value;
...
cout << value << endl; // this uses CppAnnotations::value
int
value; // error: value already defined.
return 0;
}
3.6.2.1: The `using' directive
A generalized alternative to the using-declaration is the
using-directive:
using namespace CppAnnotations;
Following this directive, all entities defined in the
CppAnnotations namespace are used as if they where declared by using
declarations.
While the using-directive is a quick way to
import all the names of
the CppAnnotations namespace (assuming the entities are declared or
defined separately from the directive), it is at the same time a somewhat
dirty way to do so, as it is less clear which entity will be used in a
particular block of code.
If, e.g., cos() is defined in the CppAnnotations namespace, the
function CppAnnotations::cos() will be used when cos() is called in
the code. However, if cos() is not defined in the CppAnnotations
namespace, the standard cos() function will be used. The using
directive does not document as clearly which entity will be used as the
using declaration does. For this reason, the using directive is
somewhat deprecated.
If Koenig lookup were called the `Koenig principle', it could have been the title of a new Ludlum novell. But, unfortunately, it isn't.
`Koenig lookup' refers to the fact that if a function is called without referencing a namespace, then the namespaces of its arguments are used to find the namespace of the function. If a function by the used name is found, then that function is used. This is called the `Koenig lookup'.
In the following example this is illustrated. The function
FBB:fun(FBB:Value v) is defined in the FBB namespace. As shown, it can
be called without the explicit mentioning of a namespace:
#include <iostream>
namespace FBB
{
enum Value // defines FBB::Value
{
first,
second,
};
void fun(Value x)
{
std::cout << "fun called for " << x << std::endl;
}
}
int main()
{
fun(FBB::first); // Koenig lookup: no namespace
// for fun()
}
/*
generated output:
fun called for 0
*/
Note that trying to
fool the compiler doesn't work: if in the
namespace FBB Value was defined as typedef int Value then
FBB::Value would have been recognized as int, thus causing the Koenig
lookup to fail.
As another example, consider the next program. Here there are two
namespaces involved, each defining their own fun() function. There is no
ambiguity here, since the argument defines the namespace. So,
FBB::fun() is called:
#include <iostream>
namespace FBB
{
enum Value // defines FBB::Value
{
first,
second,
};
void fun(Value x)
{
std::cout << "FBB::fun() called for " << x << std::endl;
}
}
namespace ES
{
void fun(FBB::Value x)
{
std::cout << "ES::fun() called for " << x << std::endl;
}
}
int main()
{
fun(FBB::first); // No ambiguity: argument determines
// the namespace
}
/*
generated output:
FBB::fun() called for 0
*/
Finally, an example in which there is an ambiguity: fun() has two
arguments, one from each individual namespace. Here the ambiguity must be
resolved by the programmer:
#include <iostream>
namespace ES
{
enum Value // defines ES::Value
{
first,
second,
};
}
namespace FBB
{
enum Value // defines FBB::Value
{
first,
second,
};
void fun(Value x, ES::Value y)
{
std::cout << "FBB::fun() called\n";
}
}
namespace ES
{
void fun(FBB::Value x, Value y)
{
std::cout << "ES::fun() called\n";
}
}
int main()
{
/*
fun(FBB::first, ES::first); // ambiguity: must be resolved
// by explicitly mentioning
// the namespace
*/
ES::fun(FBB::first, ES::first);
}
/*
generated output:
ES::fun() called
*/
cout, cin, cerr and the templates defined in the
Standard Template Library, see chapter 17) are now defined in the
std namespace.
Regarding the discussion in the previous section, one should use a using
declaration for these entities. For example, in order to use the cout
stream, the code should start with something like
#include <iostream>
using std::cout;
Often, however, the identifiers that are defined in the std namespace
can all be accepted without much thought. Because of that, one frequently
encounters a using directive, rather than a using declaration with the
std namespace. So, instead of the mentioned using declaration a
construction like
#include <iostream>
using namespace std;
is encountered. Whether this should be encouraged is subject of some
dispute. Long using declarations are of course inconvenient too. So as a
rule of thumb one might decide to stick to using declarations, up to the
point where the list becomes impractically long, at which point a using
directive could be considered.
namespace CppAnnotations
{
namespace Virtual
{
void
*pointer;
}
}
Now the variable pointer defined in the Virtual namespace, nested
under the CppAnnotations namespace. In order to refer to this variable,
the following options are available:
int main()
{
CppAnnotations::Virtual::pointer = 0;
return 0;
}
using declaration for CppAnnotations::Virtual can be
used. Now Virtual can be used without any prefix, but
pointer must be used with the Virtual:: prefix:
...
using CppAnnotations::Virtual;
int main()
{
Virtual::pointer = 0;
return 0;
}
using declaration for CppAnnotations::Virtual::pointer
can be used. Now pointer can be used without any prefix:
...
using CppAnnotations::Virtual::pointer;
int main()
{
pointer = 0;
return 0;
}
using directive or directives can be used:
...
using namespace CppAnnotations::Virtual;
int main()
{
pointer = 0;
return 0;
}
Alternatively, two separate using directives could have been used:
...
using namespace CppAnnotations;
using namespace Virtual;
int main()
{
pointer = 0;
return 0;
}
using declarations and using
directives can be used. E.g., a using directive can be used for
the CppAnnotations namespace, and a using declaration can be used for
the Virtual::pointer variable:
...
using namespace CppAnnotations;
using Virtual::pointer;
int main()
{
pointer = 0;
return 0;
}
using directive all entities of that namespace can be used
without any further prefix. If a namespace is nested, then that namespace can
also be used without any further prefix. However, the entities defined in the
nested namespace still need the nested namespace's name. Only by using a
using declaration or directive the qualified name of the
nested namespace can be omitted.
When fully qualified names are somehow preferred and a long form like
CppAnnotations::Virtual::pointer is at the same time considered too long, a namespace alias can be used:
namespace CV = CppAnnotations::Virtual; This defines CV as an alias for the full name. So, to refer to the
pointer variable the construction
CV::pointer = 0;
Of course, a namespace alias itself can also be used in a using
declaration or directive.
3.6.4.1: Defining entities outside of their namespaces
It is not strictly necessary to
define members of namespaces within a
namespace region. By prefixing the member by its namespace or namespaces a
member can be defined outside of a namespace region. This may be done at the
global level, or at intermediate levels in the case of nested namespaces. So
while it is not possible to define a member of namespace A within the
region of namespace C, it is possible to define a member of namespace
A::B within the region of namespace A.
Note, however, that when a member of a namespace is defined outside of a namespace region, it must still be declared within the region.
Assume the type int INT8[8] is defined in the
CppAnnotations::Virtual namespace.
Now suppose we want to define (at the global level) a member function
funny of namespace CppAnnotations::Virtual, returning a pointer to
CppAnnotations::Virtual::INT8. The definition of such a function could
be as follows (first everything is defined inside the
CppAnnotations::Virtual namespace):
namespace CppAnnotations
{
namespace Virtual
{
void
*pointer;
typedef int INT8[8];
INT8 *funny()
{
INT8
*ip = new INT8[1];
for (int idx = 0; idx < sizeof(INT8) / sizeof(int); ++idx)
(*ip)[idx] = (1 + idx) * (1 + idx);
return ip;
}
}
}
The function funny() defines an array of one INT8 vector, and
returns its address after initializing the vector by the squares of the first
eight natural numbers.
Now the function funny() can be defined outside of the
CppAnnotations::Virtual as follows:
namespace CppAnnotations
{
namespace Virtual
{
void
*pointer;
typedef int INT8[8];
INT8 *funny();
}
}
CppAnnotations::Virtual::INT8 *CppAnnotations::Virtual::funny()
{
INT8
*ip = new INT8[1];
for (int idx = 0; idx < sizeof(INT8) / sizeof(int); ++idx)
{
cout << idx << endl;
(*ip)[idx] = idx * idx;
}
return ip;
}
At the final code fragment note the following:
funny() is declared inside of the CppAnnotations::Virtual
namespace.
The definition outside of the namespace region requires us to use
the fully qualified name of the function and of its returntype.
Inside the block of the function funny we are within the
CppAnnotations::Virtual namespace, so inside the function fully
qualified names (e.g., for INT8) are not required any more.
Finally, note that the function could also have been defined in the
CppAnnotations region. It that case the Virtual namespace would have
been required for the function name and its returntype, while the internals of
the function would remain the same:
namespace CppAnnotations
{
namespace Virtual
{
void
*pointer;
typedef int INT8[8];
INT8 *funny();
}
Virtual::INT8 *Virtual::funny()
{
INT8
*ip = new INT8[1];
for (int idx = 0; idx < sizeof(INT8) / sizeof(int); ++idx)
{
cout << idx << endl;
(*ip)[idx] = idx * idx;
}
return ip;
}
}