C++

Histroy of C++

Computer languages have undergone dramatic evolution since the first electronic computers were built to assist in telemetry calculations during World war II.Early on,programmers worked with the most primitive computer instructions:machine langauge.These instructions were represented by long strings of ones and zeros.soon,assembles were invented to map machine instructions to human-readable and-manageable mnemonics,such as ADD and MOV.
In time,higher-level languages evloved,sush as BASIC and COBOL.These langauges let people work with something approximating words and sentences,such as let I=100.These instructions were translated back into machine language by interpreters and compilers.An intermediary then invokes a linker,which turns the object file into an executable program.

Because interpreters read the code as it is written and execute the code on the spot,interpreters are easy for the programmer to work with.Compilers,however,introduce the extra steps of compiling and linking the code,which is inconvenient.Compilers produce a program that is very fast each time it is run.However,the time-consuming task of translating the soource code into machine language has already been accomplished.
Another advantage of many compiled lanagues like C++ is that you can distribute the executable program to people who donot have the compilers.With an interpretive language,you must have the languages to run the program.

For many years,the principle goal of computer programmers was to write short pieves of code that would execute quickly.The program needed to be small,because memory was expensive,and it needed to be fast,because processing power was also expensive.As computers have become smaller,cheaper,and faster,and as the cost of memory has fallen,these priorities have changed.Today the cost of programmer's time far outweighs the cost of most of the computers in use by businesses.Well-written,easy to-maintian code is at a premium.Easy-to-maintain means that as business requirements change,the program can be extended and enhanced without great expense.

Programs

The word program is used in two ways:to describe individual instructions,or source code,created by the programmer,and to describe an entire piece of executable software.This distinction can cause enormous confusion,so we will try to distinguish between the source code on one hand,and the executable on the other.

Procedural,Structured and Object Oriented Programming

Until recently,programs were thought of as series of procedures that acted upon data.A procedure,or function,is a set specific instructions executed one after the other.The data was quite separate from procedures,and the trick in programming was to keep track of which functions called which other functions,and what data was changed.To make sense of this potentially confusing situation,structured programming was created.
The principle idea behind structured programming is as simple as the idea of divide and conquer.A computer program can be thought of as consisting of a set of tasks.Any task that is too complex to be described simply would be broken down into a set of smaller component tasks,until the tasks were sufficiently small and self-contained enough that they were easily understood.

C++ and Object_oriented Programming

C++ fully supports object-oriented programming,including the four pillars of object-oriented development:encapsulation,data hiding,inheritance,and polymorphism.Encapsulation and Data Hiding when an engineer needs to add a resistor to the device she is creating,she doesn't typically build a new one from scratch.She walks over to a bin of resistors,examines the coloured bands that indicate the properties,and picks the one she needs.The resistor is a "balck box" as far as the engineer is concerned --she doesnot much care how it does its work as long as conforms to her specifications:she doesnot need to look inside the box to use it in he design.
The property of being a self-contained unit is called encapsulation.With encapsulation,we can accomplish data hiding.Data hiding is the highly valued characteristic that an object can be used without the user knowing how the compressor works,you can use a wall-designed object without knowing about its internal data members.
Similarly,when the engineer uses the resistor,she need not know anything about the internal state of the resistor.All the propeties of the resistor are encapsulated in the resistor object;they are not spread out through the circuitry.It is not necessary to understand how the resistor works in order to use it effectively.Its data is hidden inside the resistor's casing.

C++ supports the properties of encapsulation and data hiding through the creation of user-defined types,called classes.Once created,a well-defined class acts as a fully encapsulated entity--it is used as a whole unit.The actual inner wrokings of the class should be hidden.Users of a well-defined class do not need to know how the class works;they just need to know how to use it.Inheritance and Reuse when the engineers at Acme Motors want to build a new car,they have two choices:They can start from scratch,or they can modify an existing model.Perhaps their Star model is nearly perfect,but they'd like to add a turbocharger and a six-speed transmission.The chief engineer would prefer not to start from the ground up,but rather to say,"Let's build another Star,but let's add these additional capabilities.We'll call the new model a Quasar ."A Quasar is a kind of Star,but one with new features.

C++ supports the idea of reuse through inheritance.A new type,which is an extension of an exiting type,can be decalred.This new subclass is said it derive from the existing type and is sometimes called a derived type.The Quasar is derived from the Star and thus inherits all its qualities,but can add to them as needed.The new Quasar might respond differently than a Star does when you press down on the accelerator.The Quasar might engage fuel injection and a turbocharger,while the star would simply let gasoline into its carburetor.A user,however,does not have to know about these differences.He can just"floot it,"and the right thing will happen,depending on which can he's driving.

C++ supports the idea that different objects do "the right thing" through what is called function polymorphism and class ploymorphism.Poly means many,and morph means form.Ploymorphism refers to the same name taking many forms.

How C++ Evolved

As object-oriented analysis,design and programming began to catch on,Bjarne Stroustrup took the most popular langauge for commercial software development,C and extended it to provide the features needed to facilities object-oriented programming .He created C++ and in less than a decade it has gone from being used by only a handful of developers at AT & T to being the programming langauge of choice for an estimated one million developers worldwide.It is expected that by the end of the decade,c++ will be the predomminant langauge for commercial software development.
While it is true that C++ is a superset of C,and that virtutally any legal C program is a legal C++ program,the leap from C to C++ is very significant.C++ benefited from its relationship to C for many years,as C programmers could ease into their use of C++.To really get the full benefit of C++,however,many programmers found they had to unlearn much of what they knew and learn a whole new way of conceptutalizing and solving programming problems.

A first impression of C++
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: 4.4.2).

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.


Variable and Contraints

Programs need a way to store the data they use.variable and constraints offer various ways to represent and manipulate that data.


What is a variable?

In C++ a variable is a place to store information.A variable is a location in your computer's memory in which you can store value and from which you can later retrieve that value.
Your computer's memory can be viewed as a series of cobbyholes.Each cobbyhole is one of many,many such holes all lined up.Each cubbyhole--or memory location--is numbered sequentillly.These numbers are known as memory addresses.A variable reserves one or more cubbyholes in which you may store a value.
Your variable name(for example,myVariable)is a label on one of these cubbyholes,so that you can find it easily without knowing its actual memory address.

A first impression of C++

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.

3.1: More extensions of C in C++

Before we continue with the `real' object-oriented approach to programming, we first introduce some extensions to the C programming language, encountered in C++: not mere differences between C and C++, but syntactical constructs and keywords that are not found in C.

3.1.1: The scope resolution operator ::

The syntax of C++ introduces a number of new operators, of which the scope resolution operator :: is described first. This operator can be used in situations where a global variable exists with the same name as a local variable:


#include

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 usage of the scope operator is more extensive than just this, but the other purposes will be described later.

3.1.2: cout, cin and cerr

In analogy to C, C++ defines standard input- and output streams which are opened when a program is executed. The streams are:

  • cout, analogous to stdout,
  • cin, analogous to stdin,
  • cerr, analogous to stderr.

Syntactically these streams are not used with functions: instead, data are read from the streams or written to them using the operators <<, called the insertion operator and >>, called the extraction operator. This is illustrated in the example below:



#include

void 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 remark the following:

  • The streams are declared in the header file iostream.
  • The streams 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.
  • The stream 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.
  • The operators which manipulate cin, cout and cerr (i.e., >> and <<) also manipulate variables of different types. In the above example cout <<> 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.
  • Special symbolic constants are used for special situations. The termination of a line written by cout is realized by inserting the endl symbol, rather than using the string "\n".

The streams 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:

  • Compared to the standard 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.
  • The functions 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.
  • The usage of the left-shift and right-shift operators in the context of the streams does illustrate the possibilities of C++. Again, it requires a little getting used to, coming from C, but after that these overloaded operators feel rather comfortably.

The iostream library has a lot more to offer than just cin, cout and cerr. In chapter 12 iostreams will be covered in greater detail.

3.1.3: The keyword const

The keyword const very often occurs in C++ programs, even though it is also part of the C grammar, where it's much less used.

This 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

A further 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 just prior to the keyword may not be changed. 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++.

3.1.4: References

Besides the normal declaration of variables, C++ allows `references' to be declared as synonyms for variables. A reference to a variable is like an alias; the variable name and the reference name can both be used in statements which affect the variable:


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 (`variable arguments' in Pascal-terms). E.g., in standard C, a function which increases the value of its argument by five but which returns nothing (void), needs a pointer argument:



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
}

The way in which C++ compilers implement references is actually by using pointers: in other words, references in C++ are just ordinary pointers, as far as the compiler is concerned. However, the programmer does not need to know or to bother about levels of indirection. (Compare this to the Pascal way: an argument which is declared as var is in fact also a pointer, but the programmer needn't know.)

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:

  • In those situations where a called function does not alter its arguments, a copy of the variable can be passed:


    void some_func(int val)
    {
    printf("%d\n", val);
    }

    int main()
    {
    int
    x;

    some_func(x); // a copy is passed, so
    return (0); // x won't be changed
    }

  • When a function changes the value of its argument, the address or a reference can be passed, whichever you prefer:


    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
    }

  • References have an important role in those cases where the argument will not be changed by the function, but where it is desirable to pass a reference to the variable instead of a copy of the whole variable. Such a situation occurs when a large variable, e.g., a 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

    void printperson (Person const &p) // printperson expects a
    { // reference to a structure
    printf ("Name: %s\n" // but won't change it
    "Address: %s\n",
    p.name, p.address);
    }

    Person const &getperson(int index) // get a person by indexvalue
    {
    ...
    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 function
    printperson(getperson(5)); // references, not copies
    // are passed here
    return (0);
    }

  • It should furthermore be noted here that there is another reason for using references when passing objects as function arguments: when passing a reference to an object, the activation of a copy constructor is avoided.

References also can lead to extremely `ugly' code. A function can also return a reference to a variable, as in the following example:



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 later, we have seen an example of this phenomenon at our previous discussion of the iostreams. In a statement like cout << "Hello" <<>, 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:

  • A reference cannot exist by itself, i.e., without something to refer to. A declaration of a reference like

    int &ref;

    is not allowed; what would ref refer to?

  • References can, however, be declared as external. These references were initialized elsewhere.
  • Reference may exist as parameters of functions: they are initialized when the function is called.
  • References may be used in the return types of functions. In those cases the function determines to what the return value will refer.
  • Reference may be used as data members of classes. We will return to this usage later.
  • In contrast, pointers are variables by themselves. They point at something concrete or just ``at nothing''.
  • References are aliases for other variables and cannot be re-aliased to another variable. Once a reference is defined, it refers to its particular variable.
  • In contrast, pointers can be reassigned to point to different variables.
  • When an address-of operator & 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.

3.2: Functions as part of structs

The first chapter described that functions can be part of structs . 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()
{
printf("Name: %s\n"
"Address: %s\n", name, address);
}

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.

The usage of this struct could be, e.g.:



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.

3.3: Several new data types

In C the following basic data types are available: 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.

3.3.1: The `bool' data type

In C the following basic data types are available: 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 returnvalue 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.

3.3.2: The `wchar_t' data type

The 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....

3.4: Data hiding: public, private and class

As mentioned previously C++ contains special syntactical possibilities to implement data hiding. Data hiding is the ability of one program part to hide its data from other parts; thus avoiding improper addressing or name collisions of data.

C++ has two special keywords which are concerned with data hiding: private 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) (Besides public and private, C++ defines the keyword protected. This keyword is not often used and it is left for the reader to explore.). In a struct all fields are public, unless explicitly stated otherwise.

With 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.

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).

3.5: Structs in C vs. structs in C++

At the end of this chapter we would like to illustrate the analogy between C and C++ as far as 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");

3.6: Namespaces

Imagine a math teacher who wants to develop an interactive math program. For this program functions like cos(), sin(), tan() etc. are to be used accepting arguments in degrees rather than arguments in radials. Unfortunately, the functionname cos() is already in use, and that function accepts radials as its arguments, rather than degrees.

Problems like these are normally solved by looking for another name, e.g., the functionname 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.

3.6.1: Defining namespaces

Namespaces are defined according to the following syntax:


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.

3.6.1.1: Declaring entities in namespaces

Instead of defiing 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);
}

3.6.1.2: A closed namespace

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 5). In situations where static variables or functions are necessary, the use of the anonymous namespace is preferred.

3.6.2: Referring to entities

Given a namespace and entities that are defined or declared in it, the scope resolution operator can be used to refer to the entities that are defined in the namespace. For example, to use the function cos() defined in the CppAnnotations namespace the following code could be used:


// assume the CppAnnotations namespace is declared in the next header
// file:
#include

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 radials, 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) // this uses CppAnnotations::cos()
<< ::cos(1.5) // this 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 uses 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.

3.6.3: The standard namespace

Apart from the anonymous namespace, many entities of the runtime available software (e.g., cout, cin, cerr and the templates defined in the Standard Template Library) 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

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 often encounters a using directive, rather than a using declaration with the std namespace. So, instead of the mentioned using declaration a construction like


#include

using namespace std;

is often 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.

3.6.4: Nesting namespaces and namespace aliasing

Namespaces can be nested. The following code shows the definition of a nested namespace:


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:
  • The fully qualified name can be used. A fully qualified name of an entity is a list of all the namespaces that are visited until the definition of the entity is reached, glued together by the scope resolution operator:


    int main()
    {
    CppAnnotations::Virtual::pointer = 0;
    return (0);
    }

  • A 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);
    }

  • A 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);
    }

  • A 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);
    }

  • A combination of 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);
    }

At every 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, while the 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);
}
}