Sunteți pe pagina 1din 341

Introduction To C++

BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

References


The C++ Programming Language (third edition) - Bjarne Stroustrup

BBRS IP Software Group

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

References


ISO/IEC 14882:1998 Standard for the C++ Programming Language My notes from the 1999 Software Development conference:


http://dcserver/~dcharlap/sd99

BBRS IP Software Group

Some of my examples are taken from The C++ Programming Language

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction To C++
Part 1: A better C than C
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

C Compatibility


Almost everything that is strict ANSI-C will compile under C++. Pre-ANSI language features may be incompatible. There are many new reserved words, including class, try, catch, template, namespace, public, private, protected, etc. GCC will automatically compile as C++ if the source file extension is .C, .cc, .cpp, .c++, .cp, or .cxx. A character constant is of type char, not int.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

 

BBRS IP Software Group

New Comment Style




In addition to C-style comments, C++ allows the use of a double-slash (//) to designate comments. Many C compilers support this, but this is was not part of standard C until the 1999 revision of the ISO standard (ISO/IEC 9899:1999)

BBRS IP Software Group

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Empty Argument Lists




In C, a function prototype declared with an empty argument list indicates an unspecified number of arguments. The keyword void is required to specify no arguments.
 

BBRS IP Software Group

void foo(); // Unspecified arguments void foo(...); // Unspecified arguments void bar(void); // No arguments

In C++, an empty argument list indicates no arguments. The void keyword in this context is still supported for backward compatibility.
  

void foo(); // No arguments void foo(...); // Unspecified arguments void bar(void); // No arguments

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Declare Variables Anywhere




It is no longer necessary to declare local variables at the top of a block. Declare them anywhere that makes the code easier to understand. A local variable's scope begins where it is declared and ends where its block ends. For example:


BBRS IP Software Group

void foo() { printf("entering foo\n"); int i; i = rand(); printf ("Number is: %d\n", i); }

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Loop-Local Variables
Variables can even be declared as part of a loop or conditional construct. The variable's scope will end at the end of the construct.  For example:



BBRS IP Software Group

void foo() { for (int i=0; i<100; i++) { printf("i is now %d\n", i); } // The next line is an error. i is out of scope after the end of // the loop. printf("Final value: %d\n", i); }

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Loop-Local Variables


Note that the scoping of variables declared in a loop or conditional has changed from pre-standard versions of the language. Loop- and conditional-declared variables used to exist until the end of the block containing the construct (as if the variable was declared immediately before the start of the construct.) This is not true anymore! Unfortunately, this discrepancy can result in code that is not portable to pre-standard C++ compilers. If this is a concern, you must declare variables separately from loop and conditional constructs.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

10

Loop-Local Variables


This used to be legal, but is not anymore:




void foo() { for (int i=0; i<100; i++) { printf("i is now %d\n", i); } printf("Final value: %d\n", i); }

BBRS IP Software Group

This is a workaround:


void foo() { int i; for (i=0; i<100; i++) { printf("i is now %d\n", i); } printf("Final value: %d\n", i); }

11

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

New Enum Functionality




It is legal for the same name to be declared in two different enumerated types, even with different values. When this is done, the enum's name must be specified at the point of usage, in order to resolve the ambiguity. For example:


BBRS IP Software Group

enum lsp_session_type { ip4 = 7, ip6 = 8 }; enum hop_type { ip4 = 1, ip6 = 2 };

enum lsp_session_type s; enum hop_type h; s = lsp_session_type::ip4; h = hop_type::ip4;

12

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Default Function Arguments




A function may be defined with default values in the prototype. For example:


obj *create_obj(int arg1=5, int arg2=10, int arg3=15, int arg4=20);

When called, any missing parameters are filled in with the defaults. For example, these pairs of calls are all identical:
 

BBRS IP Software Group

object *a, *b; a = create_obj(); b = create_obj(5, a = create_obj(7, b = create_obj(7, a = create_obj(3, b = create_obj(3,

10, 15, 20); 17); 17, 15, 20); 9, 87); 9, 87, 20);

13

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Default Function Arguments




All arguments with defaults must come after all arguments that do not have defaults:
  

int f(int, int=0, char* =0); // OK int g(int=0, int=0, char*); // error int h(int=0, int, char* =0); // error

BBRS IP Software Group

Note that the space after the * in the third argument is significant. Otherwise it will get parsed as the *= operator, which is a syntax error.

14

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Default Function Arguments




A default argument can not be repeated or changed in subsequent declarations in the same scope:
   

BBRS IP Software Group

void f(int x=7); void f(int=7); // error - repeated default void f(int=8); // error - changed default void g() { void f(int x=9); // OK, but hides outer declaration ... }

Declaring the name in a nested scope to hide an outer declaration can lead to all kinds of hard-to-debug errors. Don't do this if you value your sanity!
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

15

Inline Functions


Inline functions are a core language feature, not a compiler-specific extension. For obvious reasons, the inline function definition must appear in every file in which it's used. It is therefore customary to declare all inline functions in the same header file with the function prototype.

BBRS IP Software Group

16

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

References


C++ introduces the concept of references. References behave similar to pointers, but they are accessed like the objects that they refer to. A reference is denoted by an & character A reference can never be NULL - meaning reference variables must always be initialized.

BBRS IP Software Group

17

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

References


The following two functions are equivalent:




void quadruple(u_int32 *i) { *i = *i * 4; } void quadruple(u_int32 &i) { i = i * 4; }

BBRS IP Software Group

Most people consider the version using references easier to read and understand. It is also more popular among C++ programmers.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

18

References


In general, it is preferable to use references instead of pointers whenever possible. Some cases where pointers are necessary are:
  

BBRS IP Software Group

When interfacing with C code When a function is returning dynamically-allocated data When support for a NULL object/pointer is required

19

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Function Overloading


The types of the argument list of a C++ function is part of it's name. These prototypes define three different functions:
  

BBRS IP Software Group

ip_addr_t make_ip_address(u_int32 addr); ip_addr_t make_ip_address(u_int8 octet_list[4]); ip_addr_t make_ip_address(u_int8 octet1, u_int8 octet2, u_int8 octet3, u_int8 octet4);

When used, the provided argument types will tell the compiler which function to call.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

20

Function Overloading


In order to avoid confusion, two functions with the same name but different arguments should perform the same function. Functions can not be overloaded on their return types. This is illegal:
 

BBRS IP Software Group

u_int32 get_ipaddr(object_t obj); ip_addr get_ipaddr(object_t obj);

21

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Function Overloading


Function overloading can make errors in prototypes hard to track down. For instance, in SMF, if you accidentally define a persistent table using:


smdb_error foo_table::notify_imp();

Instead of:


BBRS IP Software Group

smdb_error foo_table::notify_imp( smdb_cdb_iterator::operation op);

There will be no error. You will have defined a different function which will not be called. This will probably result in unexpected behavior.

22

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Calling Convention


In order to implement function overloading (and other C++ language features), the calling convention is changed. C code can not call C++ functions because of this. To override this and use C calling conventions, declare the prototype "extern C" this way:


BBRS IP Software Group

extern "C" u_int32 get_addr(object_t *obj);

Or this way:


extern "C" { u_int32 get_addr(object_t *obj); }

23

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Calling Convention


You can also wrap entire headers this way:




extern "C" { #include <sys_inb.h> }

BBRS IP Software Group

Note that use of extern "C" only changes the calling convention. This places some restrictions on the function (for example, you can't overload it), but it is still compiled with the C++ language. You can still use all C++ language features in the function's implementation.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

24

New Memory Allocation




While malloc() and free() still exist, their use in C++ is discouraged. Instead, use the new and delete operators:
 

foo_t *f = new foo_t; delete f;

BBRS IP Software Group

Note that new and delete are not compatible with malloc() and free(). Do not delete what you allocate with malloc(), and do not free() what you allocate with new.

25

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

New Memory Allocation




The new and delete operators may also be used to allocate arrays of objects. For example:
 

foo_t *f = new foo_t[10]; delete[] f;

BBRS IP Software Group

Note the syntax change for delete. This is a requirement to prevent unwanted side effects involving destructors (to be discussed later.)

26

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

New Cast Syntax




In C++, casting is discouraged. The rules of assignment compatibility and object inheritance (to be discussed later) make most casts unnecessary. Where casts are necessary, there are four types of casts:
   

BBRS IP Software Group

Dynamic Static Reinterpret Const

Dynamic casts are part of run-time type identification and will be discussed later.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

27

Static Cast


The static_cast is similar to a C-style cast. It behaves almost identically to a C-style cast, but the types have to be somewhat compatible. (You can't for instance, use it to cast an integer onto a pointer.) Instead of writing:


BBRS IP Software Group

bar *x; foo *f = (foo *)x;

You write:


bar *x; foo *f = static_cast<foo *>(x);

28

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Reinterpret Cast


Sometimes you need to perform a cast that static_cast can't handle. For example, you may need to access something at a specific memory location, or you may be interfacing with a library that returns a pointer in an integer variable. To do this, there is reinterpret_cast, which tries to produce a value of the new type with a bit pattern as close as possible to the source type. For example:
  

BBRS IP Software Group

int pointerValue = 0x12345678; int *p = reinterpret_cast<int *>(pointerValue); unsigned char *video_buffer = reinterpret_cast<unsigned char *>(0xb8000);

29

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Const Cast


Sometimes you have a pointer to a const object and you need to get a pointer to a non-const object from it. To do this, use const_cast:


BBRS IP Software Group

foo foo_object; const foo *cf = &foo_object; foo *f = const_cast<foo *f>(cf);

Note that this only changes the pointer type, it does not change the object that is pointed to. If the object is const (and not just the pointer), casting away the const attribute may lead to undefined behavior.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

30

Casting in C++


In general, there is little need for casting in C++. most of the situations where casts were required in C have language constructs that will do the same thing more cleanly in C++. One place where casting is necessary is when casting from a void pointer onto a pointer to an actual type. This won't compile in C++:


BBRS IP Software Group

int *pi = malloc(10*sizeof(int));

but this will:




int *pi = static_cast<int *>(malloc(10*sizeof(int));

31

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

New Casts


If you must cast, using the new casts will allow the compiler to better optimize the expression. Where possible, use static_cast or const_cast and avoid reinterpret_cast. Use of the new casts instead of C-style casts also makes it easier to search for casts in code. It is nearly impossible to write a regular expression to reliably search for C-style cast expressions.

BBRS IP Software Group

32

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructor Syntax For Casts




Constructing a value of type T from value e can be expressed as T(e). For example:


BBRS IP Software Group

void f(double d) { int i = int(d); complex z = complex(d); }

For a built-in type T, the expression T(e) is equivalent to static_cast<T>(e).

33

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoid The Preprocessor When Possible




In C, the preprocessor is used for many different things, including:


   

BBRS IP Software Group

 

Header inclusion Conditional compilation Inserting code fragments Inserting code blocks Dynamic generation of function code Defining constants, types and variables

 

For the first three, the preprocessor must still be used For the rest, C++ language constructs exist which are easier to understand and are less prone to unexpected side-effects.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

34

Avoid The Preprocessor When Possible


For places where the preprocessor is used to insert code blocks, inline functions may be used instead.  Use references if an argument's value must be altered.  For example, instead of this:


BBRS IP Software Group

#define LOAD_2BYTE(PTR,VAR) { \ (VAR) = (*(PTR)++) << 8; \ (VAR) |= (*(PTR)++); } typedef unsigned char *puchar; inline void LOAD_2BYTE(puchar &ptr, u_int32 &var) { var = (*ptr++) << 8; var |= *ptr++; }

Do this:


35

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoid The Preprocessor When Possible




Another common place is when defining function-like macros, where the argument types are not known in advance. Templates (to be covered later) may be used to provide this functionality. For example, this:


BBRS IP Software Group

#define min(a,b) (((a)<(b)) ? (a) : (b))

May be rewritten as this:




template<class T> inline T min(T a, T b) { return ((a<b) ? a : b); }

36

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoid The Preprocessor When Possible




This version:


template<class T> inline T min(T a, T b) { return ((a<b) ? a : b); } The template will make sure that a and b are compatible types (resulting in compiler errors if they are not.) The problem of macro arguments being evaluated multiple times (resulting in multiple-execution of side-effects) doesn't occur.

has many advantages aside from not being a macro:


BBRS IP Software Group


37

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoid The Preprocessor When Possible




For places where the preprocessor is used to define constants, use either an enum or a const int. Instead of these:


#define CTYPE_HOP_IP4 (1) #define CTYPE_HOP_IP6 (2) #define FILL_PATTERN (0xdeadbeef) enum ctype_hop { ip4 = 1, ip6 = 2 }; const u_int32 fill_pattern = 0xdeadbeef;

BBRS IP Software Group

Use these:


38

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoid The Preprocessor When Possible




The mechanism for declaring const variables is much more optimized in C++. If the value of a const variable is known at compile time and is known not to change, the compiler does not typically assign storage for that variable. For example:


BBRS IP Software Group

const const const extern const const

int c1 = 1; int c2 = 2; int c3 = foo(3); int c4; int *p = &c2;

In these examples, c1 may not have storage assigned to it. c2 will, because p points to it, c3 and c4 will because the values are not known at compile time.

39

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoid The Preprocessor When Possible


Types that are declared using macros may be declared using the template mechanism (to be covered later).  For example, this:



BBRS IP Software Group

#define MY_STRUCT(N) struct { char *foo; int bar[N]; }

\ \ \ \ \

May be rewritten as:




40

template <int i> struct mystruct { char *foo; int bar[i]; }; 2004. The Copyright in this document belongs to Marconi and no part of
this document should be used or copied without their prior written permission.

Mutable


BBRS IP Software Group

Many times, you have a struct where you always want to be able to write to a specific field, even when the struct is declared const. In C, you have to either not declare it const, or explicitly cast away const when accessing the field. Both of these are bad. Not making the struct const will not allow the compiler to catch attempts to write to the struct. Explicitly casting away const will not allow the compiler to catch mistakes if the wrong field is accessed. These also prevent the compiler from making optimizations that depend on the const-nature of the struct.

41

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Mutable


C++ introduces the mutable keyword to work around this. For example:


struct foo { int int mutable int int } a; b; c; d;

BBRS IP Software Group

A mutable field is always writable, even when the struct is const. For example:
  

const foo f = {1, 2, 3, 4}; f.c = 7; // OK - foo.c is mutable f.d = 9; // error - f is still const

42

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction to C++
Part 2: New Language Concepts
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Introduction to C++
Structs and classes
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Structs


In C++, the definition of a struct has changed. In addition to storing data ("data members") in a struct, you can also associate functions ("member functions") with a struct. A member function is also known as a method. A method's name need only be unique within the context of the struct.

BBRS IP Software Group

45

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


It is common and often useful for members of different classes to have the same name. Methods are called through an instance of, or pointer to, or reference to the struct. The act of bundling together data and methods designed to act on that data is known as encapsulation. Memory allocated to contain a struct's data is often referred to as an object or as an instance of the struct.

BBRS IP Software Group

46

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


Here's an example of a possible IP address struct:




struct ip_addr { u_int32 addr; // Host order u_int32 u_int32 void void }; network_order(); host_order(); set_network_order(u_int32); set_host_order(u_int32);

BBRS IP Software Group

  

 
47

No typedef is required The final semicolon is mandatory!


2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


The methods are defined like ordinary functions, but with the :: operator to indicate the struct they belong to:


u_int32 ip_addr::network_order() { return htonl(addr); } u_int32 ip_addr::host_order() { return addr; } void ip_addr::set_network_order(u_int32 newaddr) { addr = ntohl(newaddr); } void ip_addr::set_host_order(u_int32 newaddr) { addr = newaddr; }

BBRS IP Software Group

48

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


Methods are called by dereferencing the struct using the same "." and "->" operators that are used for accessing data members.


ip_addr a; ip_addr *b;

// Assume this points to something

BBRS IP Software Group

a.set_network_order(0x78563412); b->set_host_order(0x12345678); printf("a is 0x%x\n", a.host_order()); printf("b is 0x%x\n", b->host_order());

49

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


Methods may be defined in-line with the struct definition:




struct ip_addr { u_int32 addr; u_int32 network_order() { return htonl(addr); } u_int32 host_order() { return addr; } ... };

BBRS IP Software Group

When declared this way, the methods will be inline when they are used.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

50

Structs


Methods can access the data members of their struct without using a pointer to the struct. The memory used is the struct that the method is called from:


ip_addr *a = new ip_addr; a->set_host_order(0x10111213); printf("0x%x\n", a->host_order());

BBRS IP Software Group

51

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


If a method actually needs a pointer to the struct it's working on, it is available. It is always named this. These two implementations are identical:


BBRS IP Software Group

u_int32 ip_addr::host_order() { return addr; } u_int32 ip_addr::host_order() { return this->addr; }

Most of the time, there will be no need to explicitly use the this pointer.

52

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


Note that methods may access all members, even ones defined afterwards in the struct. This is perfectly legal (but a little confusing):


BBRS IP Software Group

struct ip_addr { u_int32 network_order() { return htonl(addr); } u_int32 host_order() {return addr; } ... u_int32 addr; }

53

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Structs


In order to avoid the confusion of seeing a member used before it is declared in the file, you can declare inline methods after the struct in the header file:


BBRS IP Software Group

struct ip_addr { u_int32 network_order(); u_int32 host_order(); ... u_int32 addr; } inline u_int32 ip_addr::network_order() { return htonl(addr); } inline u_int32 ip_addr::host_order() { return addr; }

54

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Static Data Members




A static data member is shared by all instances of a struct. For example:




struct foo { int a; static int b;

BBRS IP Software Group

};


foo *f = new foo; foo *g = new foo; f->a f->b g->a g->b = = = = 1; 2; 3; 4;

 

// This is a unique variable // This overwrites f->b

55

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Const Methods


A method may be declared const. This means the method may not modify any data members in the struct. For example:


BBRS IP Software Group

struct ip_addr { u_int32 addr; u_int32 network_order() const { return htonl(addr); } u_int32 host_order() const { return addr; } ... };


56

The compiler can use this in optimizations.


2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Data Protection


With the previous example, the data member addr may be accessed without the accessor member functions. To prevent that, we can declare it private:


BBRS IP Software Group

struct ip_addr { private: u_int32 addr; ... };

57

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Data Protection


Private members (data and functions) may only be accessed from that struct's methods. Public members may be accessed by anything.

BBRS IP Software Group

58

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Data Protection


This means that with the following struct, we can't call the accessors methods from code declared outside the struct:


BBRS IP Software Group

struct ip_addr { private: u_int32 addr; u_int32 u_int32 void void }; network_order(); host_order(); set_network_order(u_int32 newaddr); set_host_order(u_int32 newaddr);

59

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Data Protection


To allow access, we must declare the accessor methods public:




BBRS IP Software Group

struct ip_addr { private: u_int32 addr; public: u_int32 u_int32 void void };

network_order(); host_order(); set_network_order(u_int32 newaddr); set_host_order(u_int32 newaddr);

60

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Data Protection


It is very common to declare data members private. This helps to enforce encapsulation (forcing all access to go through accessor methods.) Because of this, the keyword class was invented. A class is a struct, where members are private by default instead of public.

BBRS IP Software Group

61

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Data Protection


There is no difference between a class and a struct other than the default protection. This is exactly the same as our previous example:


BBRS IP Software Group

class ip_addr { u_int32 addr; public: u_int32 u_int32 void void };

network_order(); host_order(); set_network_order(u_int32 newaddr); set_host_order(u_int32 newaddr);

62

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Data Protection


In addition to public and private, there is a third kind of protection, called protected. A protected member may be accessed by methods of that class, and by methods of subclasses, but not by methods of unrelated classes.

BBRS IP Software Group

63

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Friends


Sometimes, the data protection keywords are insufficient. For example, imagine classes that describe a Vector (an array of floats) and a Matrix (an array of Vectors):


BBRS IP Software Group

class Vector { float v[4]; }; class Matrix { Vector v[4]; };

64

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Friends


Now, suppose we want to write a function that multiplies a Vector by a Matrix:




Vector multiply(const Matrix &m, const Vector &v) { Vector r; for (int i=0; i<4; i++) { r.v[i] = 0; for (int j=0; j<4; j++) r.v[i] += m.v[i].v[j] * v.v[j]; } return r; }

BBRS IP Software Group

65

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Friends


As we can see, this multiply function requires direct access to the data members of both the Vector and Matrix classes. We could just make those members public, but that would allow all other code to have access. We could make the function a method on one of the classes, but that would not allow us access to the other class's data, and one function can't be a method of two classes.

BBRS IP Software Group

66

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Friends


The solution here is to declare the multiply function to be a friend of the two classes as a part of the class definition.


BBRS IP Software Group

class Matrix; // Forward declaration class Vector { float v[4]; friend Vector multiply(const const }; class Matrix { Vector v[4]; friend Vector multiply(const const };

Matrix&, Vector&);

Matrix&, Vector&);


67

A friend can access private members of the class that has declared it a friend.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Friends


You can also declare an entire class to be a friend of another class (not just a function):


class foo; // Definition is unimportant class bar { // Definition is unimportant friend class foo; };

BBRS IP Software Group

All methods of foo can access all of bar's private members.


2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

68

Friends


Although it is sometimes necessary to declare methods and classes as friends, it usually is not. If you find yourself having to declare a lot of friends, then you probably have a design error in your program. Go back and think about what changes you can make to your classes that may make the friends unnecessary.

BBRS IP Software Group

69

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors


A method whose name is the same as the class name is a constructor. It is automatically executed when the object is allocated. Constructors have no return type. Constructors should be public, since objects are normally created by code that's not part of the class.


BBRS IP Software Group

class ip_addr { u_int32 addr; public: ip_addr() { addr = 0x12345678;} ... };

70

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors


Using the previous example:




ip_addr *a = new ip_addr; printf("0x%x\n", a->host_order());

This will print out "0x12345678" The value is assigned by the constructor which automatically executed when the new operator was called.

BBRS IP Software Group

71

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors


Constructors may accept arguments and may be overloaded. For example:




class ip_addr { u_int32 addr; public: ip_addr() { addr ip_addr(u_int32 newaddr) { addr ip_addr(ip_addr& newaddr) { addr ip_addr(ip_addr* newaddr) { addr };

BBRS IP Software Group

  

= = = =

0; } newaddr; } newaddr.addr; } newaddr->addr; }

72

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors


Arguments to constructors are passed as arguments to the new operator:


  

BBRS IP Software Group

ip_addr ip_addr ip_addr ip_addr

*a *b *c *d

= = = =

new new new new

ip_addr; ip_addr(0x12345678); ip_addr(*b); ip_addr(b);

They're also used for creating objects as local variables and temporary variables:
  

ip_addr e(0xdeadbeef); ip_addr f(e); ip_addr g = ip_addr(0xfeedface);

73

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors


If a class has a constructor, you must use it when allocating objects.




BBRS IP Software Group

class aClass { int x; public: aClass(int newX) { x = newX; } };

These lines are illegal. They require a non-existent default constructor:




aClass a; aClass *b = new aClass;

74

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Explicit Constructors


By default, a single-argument constructor also implies a conversion from that type during initialization. For some cases, this is exactly what you want. For example:


class complex { double f, i; public: complex(float new_f) { f=new_f; i=0; } ... }

BBRS IP Software Group

complex z = 2;

// initialize with complex(2)

75

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Explicit Constructors


In other cases, this is not at all what you want. For example:


class string { char *s; public: string(int n) { s = new char[n+1]; } string(char *news) { s = new char[strlen(news)+1]; strcpy(s, news); }

BBRS IP Software Group

} string s = 'a';

// Error: Makes an empty string // of int('a') characters wide

76

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Explicit Constructors


Declaring a constructor explicit solves this:




class string { char *s; public: explicit string(int n); string(char *news); }

BBRS IP Software Group

77

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Explicit Constructors


With the explicit keyword on the integer constructor, we get the following results:
  

BBRS IP Software Group

  

string string string string string string

s1 = 'a'; // Error - this constructor is explicit s2 = 10; // Error - it's the same constructor s3(10); // OK - this is an explicit construction s4 = string(10); // OK s5 = "Foo"; // OK s6("Bar"); // OK

78

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Destructors


A destructor is a method that is automatically executed when an object is deleted. Its name is the name of the class, prefixed with a tilde (~). Destructors typically free any dynamically-allocated objects that are associated with the object being freed.

BBRS IP Software Group

79

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors and Destructors




When you use the array-allocate version of new and delete, the constructor and destructor will be called for every element of the array. Constructors and destructors are run for all objects, no matter how they are created or destroyed. This includes dynamic (heap) allocation, static, global or temporary. Watch out for unanticipated side effects from temporary object instantiation.


BBRS IP Software Group

This will create and destroy a temporary object:




ip_addr a = ip_addr(0xdeadbeef); ip_addr b(0xdeadbeef);

This does not:




80

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors and Destructors




Constructors for all objects will run as soon as the object is allocated, even if it is not allocated with the new operator. For objects declared globally, this will happen before the first line of main()! Globally-declared objects in a single source file are constructed in the order they appear in the file. Globallydeclared objects declared in separate source files are not constructed in a deterministic order. For static objects, the constructor will run the first time the object comes into scope.

BBRS IP Software Group

81

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors and Destructors




Destructors for all objects will run when the object is freed, even if this is due to it going out of scope. Objects that are freed simultaneously (like local variables that go out of scope) are destroyed in the reverse order of their construction. For static and global objects, this may not be deterministic, since the order of construction is not entirely deterministic. The time of destruction for global and static objects is not defined.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

82

Constructors and Destructors




BBRS IP Software Group

When used as local variables, all object constructors will run when the object enters scope, in the order they are declared, and destructors will run when the object leaves scope. Destructors run in the opposite order from the constructors. For example:  void f(int i) aa, bb and dd are constructed { (in that order) whenever f() is Table aa; called. If i>0, cc will be Table bb; constructed and destroyed if (i>0) before dd is constructed. Before { Table cc; exiting, dd, bb and aa will be } destroyed (in that order)
Table dd; }
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

83

Constructors and Destructors




When objects are assigned to each other, a memberwise copy is made (similar to how C structs are assigned to each other.) This is often not what you want:


BBRS IP Software Group

void f() { Table t1; Table t2 = t1; Table t3; t3 = t2; }

// initialization

// assignment

t2 and t3 will be clones of t1, including any internal pointers. You probably really wanted a new object containing the same data.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

84

Constructors and Destructors




A copy constructor and an overload of the assignment operator can solve this problem:


BBRS IP Software Group

struct Table { ... Table(const Table&); Table& operator= (const Table&); }

The copy constructor is used during initialization. The assignment operator overload is used for assignment. (Operator overloading will be discussed later.) Implementations may then do what's appropriate for properly copying the object's data.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

85

Constructors and Destructors




When overloading assignment, don't forget to consider the possibility of self-assignment (e.g. a=a;). You probably want to make the assignment a no-op when this happens:


BBRS IP Software Group

Table& Table::operator=(const Table& t) { if (this != &t) { // do the data copying here } return *this; }

86

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Constructors and Destructors




If you allocate an object with malloc(), the constructors will not execute. Internal housekeeping data for handling virtual methods and related things will not be initialized. Similarly, destructors will not execute if memory is freed using free(). In other words, don't do this. Don't use malloc() and free() unless the object has no methods and will be allocated or freed by C code. Otherwise, use new and delete.

BBRS IP Software Group

87

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Address List Example




For an example of constructors and destructors, here's a struct containing an array of an unknown number of IP addresses. The array is allocated by the constructor. It is freed by the destructor. Note that this will use the array-allocation version of the new and delete operators.

BBRS IP Software Group

88

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Address List Example




class addr_list { int count; u_int32 *addrs; public: addr_list(int newcount) { count = newcount; addrs = new u_int32[newcount]; } ~addr_list() { if (addrs != NULL) delete[] addrs; }

BBRS IP Software Group

...
}

89

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Unions


A named union is defined as a struct, where every member has the same address. A union may have member functions, but no static members. Because the compiler can't know which member is in use at any given time, no union members may have constructors or destructors.

BBRS IP Software Group

90

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Subclasses


It is possible to define an object class in terms of differences from another object. This is known as inheritance. For instance, an IP header with options is an ordinary IP header with an extra field.

BBRS IP Software Group

91

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

IP Header example


Here's an IP header:


BBRS IP Software Group

struct ip_header { u_int8 version; u_int8 hdr_length; u_int8 tos; u_int16 payload_len; u_int16 packet_id; u_int16 fragmentation_info; u_int8 ttl; u_int8 l4pid; u_int16 checksum; u_int32 source_addr; u_int32 dest_addr; };

92

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

IP Header example


And here's a struct that inherits from it:




BBRS IP Software Group

struct ip_header_with_options : public ip_header { u_int8 options_length; u_int8 options[40]; ];

The subclass ip_header_with_options inherits all of the fields from ip_header.

93

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Subclasses


A pointer to a subclass is assignment compatible with a pointer its parent (or grandparent, etc.) class. This is perfectly legal:


ip_header *hdr = new ip_header_with_options;

BBRS IP Software Group

This assignment compatibility is not commutative, however. This is not legal:




ip_header_with_options *hdr = new ip_header;

94

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Methods and Subclasses




Methods (including constructors and destructors) are inherited just like data members:


BBRS IP Software Group

struct ip_header { ... u_int32 get_source_addr() { return source_addr; } } ip_header_with_options *h; u_int32 addr = h->get_source_addr();

95

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Method Overrides


It is possible for a class and a subclass to define methods with the same name:


struct ip_header {

... BBRS IP Software Group


void printsomething() { printf("ip_header\n"); } };


struct ip_header_with_options : public ip_header {

...
void printsomething() { printf("ip_header_with_options\n"); } };
96
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Method Overrides


When a class and a subclass implement the same method, the one that is called depends on the type of the pointer or reference used:


ip_header_with_options h; ip_header *hh = &h; // This prints "ip_header_with_options" h.printsomething(); // This prints "ip_header" hh->printsomething();

BBRS IP Software Group

97

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Virtual Methods


Most of the time, however, it is more useful to execute the method that corresponds with the class of the data instead of the class of the pointer. This can be done by declaring the method virtual.

BBRS IP Software Group

98

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Virtual Methods


This is the previous class hierarchy, with virtual methods:




struct ip_header {

... BBRS IP Software Group


virtual void printsomething() { printf("ip_header\n"); } }


struct ip_header_with_options : public ip_header {

...
virtual void printsomething() { printf("ip_header_with_options\n"); } }
99
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Virtual Methods


This is the previous example, with virtual methods:




ip_header_with_options h; ip_header *hh = &h; // This prints "ip_header_with_options" h.printsomething(); // This also prints "ip_header_with_options" hh->printsomething();

BBRS IP Software Group

100

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Virtual Methods


Once a method is declared virtual, subclasses do not need to continue using the virtual keyword. The virtual nature of the method is inherited and can not be removed by subclasses. Nevertheless, it is a good idea to continue using it, because the resulting code is easier to understand.

BBRS IP Software Group

101

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pure Virtual Classes




Sometimes, you may have to define a class that will never be instantiated, in order for two related classes to share a common parent. In order to prohibit instantiation of such classes, you can declare a virtual method pure virtual, by suffixing "=0" to the declaration.

BBRS IP Software Group

102

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pure Virtual Classes




For example, imagine a class representing an interface. You have two subclasses - numbered and unnumbered. Because they share common methods, they must share a common superclass. But you don't want any code allocating instances of the superclass (the undifferentiated interface.)

BBRS IP Software Group

103

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pure Virtual Classes




Here's the class definition for the superclass.




class interface { int index; public: virtual int get_index() { return index; } virtual u_int32 get_addr() = 0; virtual u_int32 get_ifid() = 0; };

BBRS IP Software Group

104

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pure Virtual Classes




Here's the subclass for numbered interfaces:




class numbered_interface : public interface { u_int32 addr; public: virtual u_int32 get_addr() { return addr; } virtual u_int32 get_ifid() { return 0; } }

BBRS IP Software Group

105

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pure Virtual Classes




And here's the subclass for unnumbered interfaces:




class unnumbered_interface: public interface { u_int32 ifid; public: virtual u_int32 get_addr() { return get_router_addr(); } virtual u_int32 get_ifid() { return ifid; } }

BBRS IP Software Group

106

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pure Virtual Classes




With this example, any attempt to instantiate an interface will result in a compile-time error. There is no problem instantiating either subclass. They can still be assigned to pointers to interface, and the virtual methods can still be called through those pointers. For example:
   

BBRS IP Software Group

interface *i, *j, *k; i = new interface; // Illegal - pure virtual j = new numbered_interface; // OK k = new unnumbered_interface; // OK

107

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pure Virtual Classes




A pure virtual method is inherited, just like all other methods. You don't have to override all pure virtual methods, but if you don't, your subclass will also be pure virtual, and you won't be able to instantiate it.

BBRS IP Software Group

108

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Calling the Superclass




Sometimes, a method must call a method of the parent class. Simply calling the method by name will call the local class version. To get a parent class version, you must name the class, using the :: operator.

BBRS IP Software Group

109

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Calling the Superclass




For example, suppose the IP header classes have a "dump" function:




BBRS IP Software Group

struct ip_header { ... virtual void dump(); }; struct ip_header_with_options : public ip_header { ... virtual void dump(); };

110

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Calling the Superclass




void ip_header::dump() { printf("version: %d\n", version); printf("header length: %d\n", hdr_length);

...
printf("destination address: 0x%x\n", dest_addr); }

BBRS IP Software Group

void ip_header_with_options() { ip_header::dump(); printf("options length: %d\n", options_length); for (int i=0; i<options_length; i++) printf("options[%d]: %d\n", i, options[i]); }

111

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Special Case for Constructors




When constructors run, the superclass constructor always executes before the subclass constructor. So how do you pass parameters to the superclass constructor? The answer is you specify it using the : operator as part of the method's implementation header.

BBRS IP Software Group

112

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Special Case for Constructors




For example, here is an ip_header constructor that allows you to initialize the source and destination addresses, and a default constructor:


BBRS IP Software Group

struct ip_header { ... ip_header() {}; ip_header(u_int32 src, u_int32 dst) { source_addr = src; dest_addr = dst; } }

113

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Special Case for Constructors




In order for ip_header_with_options to call the nondefault superclass constructor, we declare its constructor this way:


BBRS IP Software Group

struct ip_header_with_options : public ip_header { ... ip_header_with_options(u_int32 src, u_int32 dst) : ip_header(src, dst) {}; }

114

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Special Case for Constructors




It's worth noting that ordinary variables can be intialized using the same technique, since basic variables can be initialized with "constructor syntax":


BBRS IP Software Group

struct ip_header { ... ip_header() {}; ip_header(u_int32 src, u_int32 dst) : source_addr(src), dest_addr(dst) {} }

The main difference is that this example uses initialization and the previous example uses assignment. For basic non-const types, there is little difference between the two.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

115

Virtual Destructors


As you may have figured out by now, the destructor that is called when the delete operator is used should be based on what the data is, and not what the pointer is. Because of this, you should always declare your destructor methods virtual:


BBRS IP Software Group

virtual void ~ip_header();

116

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Static Methods


Like data members, methods may be declared static. A static method is not really a method of the class. When it is called, it does not have a this pointer, and therefore can't access an object's members without an explicit pointer to the object. The difference between a static method and a nonmember function is that a static method's name is associated with a class. All the usual protection and scoping rules still apply.

BBRS IP Software Group

117

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Static Methods


This may be convenient when building libraries. Especially when declaring functions that are closely related to a class, but don't actually act on objects of that class. Static methods may be called using an object pointer or reference (like normal methods.) They may also be called by explicitly naming the class. A method may not be both static and virtual.

BBRS IP Software Group

118

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Static Methods


For example, let's add a static method to the ip_address class for identifying the class:


BBRS IP Software Group

struct ip_addr { ... static void print_classname() { printf("ip_addr\n"); } }

These two invocations are equivalent




ip_addr *a = new ip_addr; a->print_classname(); ip_addr::print_classname();

 

119

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Inheritance Protection


So far, we've always used the public keyword when one class inherits from another class. It is also possible to use private or protected in inheritance:
   

BBRS IP Software Group

class class class class

foo; bar1 : public foo; bar2 : protected foo; bar3 : private foo;

This keyword, when used in this capacity, is known as an access specifier because it specifies who may access the base class's members.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

120

Public Inheritance


When a derived class (D) inherits from a base class (B) with the public access specifier (the most common way):
  

All code may convert a D* pointer to a B* pointer without a cast. All code can access public members inherited from B. Only friends and members of D and friends and members of classes that inherit from D can access protected members inherited from B.

BBRS IP Software Group

121

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Protected/Private Inheritance


When a derived class (D) inherits from a base class (B) with the protected access specifier:


BBRS IP Software Group

Only friends and members of D and friends and members of classes that inherit from D can convert a D* pointer to a B* pointer without a cast. Only friends and members of D and friends and members of classes that inherit from D can access protected and public members inherited from B.

When a derived class (D) inherits from a base class (B) with the private access specifier:


Only friends and members of D can convert a D* pointer to a B* pointer without a cast. Only friends and members of D can access protected and public members inherited from B.

122

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Inheritance Protection


Use of an access specifier when inheriting is optional. If it is omitted, a default is used:


 

A class inherits with a default access specifier of private A struct inherits with a default access specifier of public

BBRS IP Software Group

Regardless, you should always provide your own access specifier whenever you derive subclasses, because it makes the code easier to understand. And most of the time, the default (private inheritance of classes) is not what you want.

123

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction To C++
Exceptions
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Exceptions


It is common for functions to return error codes. In order to be safe, every time such a function is called, you must check the return code for an error. If the caller can't handle the error, it must pass the error to the calling function. This can be awkward. Especially if the code that handles the error is many calls removed from the code that generates the error.

BBRS IP Software Group

125

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Exceptions


Exceptions solve this problem When an exceptional condition (like an error) is detected, the code will generate (aka "raise" or "throw") an exception. The exception will repeatedly pop (unwind) the call stack until an exception handler is found. If no handler is found, the program will abort.

BBRS IP Software Group

126

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Exceptions in C


In C, you can accomplish similar behavior through careful use of the setjmp() and longjmp() functions. In C++, however, this won't work, because longjmp() doesn't call destructors on all the automatic objects that get freed by the popped stack frames. Exceptions will call destructors on automatic objects while the stack unwinds.

BBRS IP Software Group

127

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

The throw Keyword




Exceptions are generated with the throw keyword. Any kind of object may be thrown as an exception. For example:


BBRS IP Software Group

  

throw throw throw throw

5; "abcdef"; vector(10,15); global_ri;

// integer // string // object // pointer

128

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

The try And catch Keywords




Exceptions are handled using blocks defined by the try and catch keywords. To catch an exception, wrap the potential exceptiongenerating code in a try block and put the exception handler in a catch block:


BBRS IP Software Group

try { do_something(); } catch (int i) { printf("Caught exception %d\n", i); }

129

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Catching Exceptions
 You

may catch many types of exceptions for one try block:




try { do_something(); } catch (int i) { printf("Caught int exception %d\n", i); } catch (vector v) { printf("Caught vector exception %d,%d\n", v.dx(), v.dy()); }

BBRS IP Software Group

130

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Catching Exceptions


If the type of the exception thrown doesn't match what you're catching, then you won't catch it. The exception will continue to propagate up the call stack. If you must be able to catch an unknown exception type, you can do it by specifying an ellipsis for the catch argument.


BBRS IP Software Group

... catch (...) { printf("Caught unknown exception\n"); }

131

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Catching Exceptions


Assignment compatibility still applies. If your code is written to catch a class, and a subclass (that was derived with a public access specifier) of that class is thrown, you will catch it. This allows you to define a hierarchy of exception classes for catching errors at various levels of granularity. The standard C++ library defines and uses a hierarchy of many such exceptions.

BBRS IP Software Group

132

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Standard Exceptions


The standard C++ library makes extensive use of exceptions to indicate errors of various kinds. The standard exceptions are defined as a class hierarchy:
exception

BBRS IP Software Group

logic_error

runtime_error

domain_error invalid_argument length_error out_of_range

bad_alloc bad_cast bad_exception bad_typeid ios_base::failure

overflow_error range_error underflow_error

133

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Re-throwing Exceptions


Sometimes, you can't completely handle an exception. You want to handle what you can, and then let the exception continue propagating up the stack. The throw keyword, when used without any parameters from within a catch block will do this:


BBRS IP Software Group

try { do_something(); } catch (...) // catch everything { cleanup_some_state(); throw; // re-throw the exception }

134

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction To C++
Part 3: Advanced Concepts
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Introduction To C++
Operator Overloading
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Operator Overloading


All the standard operators may be overloaded just like functions. When used properly, this can make your code easier to understand. It can also be abused, making code an unreadable mess.

BBRS IP Software Group

137

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Operator Overloading Example




As an example of where operator overloading may be useful, imagine a class for a two-dimensional vector:


class vector { double deltax, deltay; public: vector(double newdx, double newdy) { deltax=newdx; deltay=newdy; } void set_dx(double newdx) { deltax = newdx; } void set_dy(double newdy) { deltay = newdy; } double dx() { return deltax; } double dy() { return deltay; } }

BBRS IP Software Group

138

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Operator Overloading Example




Now, suppose we want to add two vectors together. We could write a function to do it:


BBRS IP Software Group

vector add_vectors(vector &v1, vector &v2) { return vector(v1.dx() + v2.dx(), v1.dy() + v2.dy()); }

But this is awkward to use

139

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Operator Overloading Example




Instead, we can override the + operator:




vector operator+(vector &v1, vector &v2) { return vector(v1.dx() + v2.dx(), v1.dy() + v2.dy()); }

BBRS IP Software Group

Usage is just like any other use of +:




vector v1(1,5); vector v2(10,3); vector v3(0,0); v3 = v1 + v2; // v3 is now {dx=11, dy=8}

140

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Operator Overloading In A Class




Most (but not all) operator overloads can also be implemented as a method on the class. The previous operator overload is the same as this:


BBRS IP Software Group

class vector { ... vector operator+(vector &v) { return vector(deltax + v.deltax, deltay + v.deltay); } }

141

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Rules of Overloading


Most (but not all) operators may be overloaded New operators may not be defined The number of arguments to an operator may not be changed Precedence rules may not be changed The same operator may be overloaded many times, with different argument types (just like functions)

BBRS IP Software Group

142

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Operator Overloading


Some very commonly-overloaded operators include:


  

BBRS IP Software Group

 

Assignment (=) Comparison (==, !=, <=, >=, >, <) Arithmetic (+, -, *, /, %, +=, -=, *=, /=, %=, ++, --) (for numeric classes) Boolean (|, &, ^, <<, >>, |=, &=, ^=, <<=, >>=, ||, &&) (for Boolean and bit-field classes) Streaming I/O (<<, >>) (when applied to iostream classes) Concatenation (+, +=) (when applied to sequences)

143

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Exercise Restraint When Overloading




PLEASE don't go overboard. Before you decide to define an operator overload, think carefully what code using your overload will look like. If the meaning isn't intuitively obvious, use an ordinary function or method call instead. Many new programmers, after learning about operator overloading, like to use it all over the place. More often than not, the resulting code is impossible to understand. Well thought out operator overloads can make code easy to understand. Overloads that are not well thought out can make code very difficult to understand.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

144

Introduction To C++
Templates
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Linked List Example


Templates are a mechanism for dynamically generating types and code as-needed.  For example, let's define a class for a doubly-linkedlist node:



BBRS IP Software Group

class list_node { list_node *next, *prev; public: list_node *get_next() { return next; } list_node *get_prev() { return prev; } void insert_before(list_node *node) { node->prev=prev; node->next=this; prev->next=node; prev=node; } }

146

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Linked List Example




But an empty list node like this is not normally useful. We usually want to keep a list of some kind of data. One solution (which is commonly done in the RCI code) is to put a list node into a larger struct and then do some pointer manipulation in order to access the data (outer struct) from a pointer to the list node (inner struct). There are two big problems with this approach:
 

BBRS IP Software Group

It is difficult to understand the code when reading it later. It requires explicit casting and pointer arithmetic, both of which can be sources of errors that the compiler can't catch.

147

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Linked List Example




Another solution is to put a (void *) pointer in the list_node class, and have it point to the data. This is easier to understand, but it still has many problems:


BBRS IP Software Group

 

We must take steps to make sure that the data objects and the list nodes don't get out of sync with each other. We are still being forced to cast pointers in order to access the data. The compiler can't protect us against mistakes if we cast incorrectly. The pointer can be NULL There are now two objects that must be allocated and freed together.

148

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Linked List Example




A third solution is to make lots of variant list_node classes that contain the appropriate data.


BBRS IP Software Group

This is a lot of maintenance. If we change our implementation, we've got a lot of classes to change. This can introduce errors - especially the cut-and-paste kind that take a long time to clean up. We (probably) have to declare these classes separately from the places where we're using them. In a large project, it will be easy to forget which ones we need and which ones we don't need. Cleaning up no-longer-used list class variants can be time consuming. Every time we need to change the data portion of a list, we must change or create a class variant. This can be laborintensive and may introduce errors.

149

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Linked List Example




A fourth solution is to make lots of variant list_node classes that are all subclasses of a common list_node class. This solves some of the variant-classes problems, but still doesn't solve them all.
 

BBRS IP Software Group

The classes still will be declared separately from usage We still have maintenance issues if we change the data of a list. We also end up introducing an inheritance relationship between different list-node classes where there may not logically be one. For instance, a list of integers should not have any nodes that are used to keep a list of IP instances. If we declare a pointer to list_node for something, the compiler can't protect us from using pointers to the wrong list_node subclass.

150

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Reason For Templates




BBRS IP Software Group

Templates solve all of these problems by letting you declare the class as a template, with placeholders for the missing parts that are provided at the location the variables are declared. Common functionality is declared exactly once - in the template definition, so it only must be modified in one place when changes are necessary. The classes that get generated do not have an inheritance relationship between them, so you can't blindly mix them together. Classes are dynamically generated at the point of usage, so unused classes don't clutter up object code and library files.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

151

Linked List Template Example




A template version of the doubly-linked-list node might look like this:




template<class C> class list_node { list_node<C> *next, *prev; public: C data; list_node<C> *get_next() { return next; } list_node<C> *get_prev() { return prev; } void insert_before(list_node<C> *node) { node->prev=prev; node->next=this; prev->next=node; prev=node; } }

BBRS IP Software Group

152

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Linked List Template Example




Now, to make a list of integers (or floats, or objects), you just declare your nodes using the template. For example
   

list_node<int> list_node<float> list_node<foo> list_node<foo *>

// List of integers // List of floats // List of objects // List of object pointers

BBRS IP Software Group

These are dynamically-generated types. All are of the form of the list_node template, but with the type in the brackets substituted for C in the template. These types are separate classes that do not have an inheritance relationship among them.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

153

Template Parameters


There may be multiple template parameters. For instance, a template to declare an unknown-size array of unknown types might be declared as:


BBRS IP Software Group

template<class C, int i> struct myarray { C data[i]; int size; myarray() { size = i; } }

And then used as:


 

myarray<int, 10> a; // 10 integers myarray<foo, 1000> b; // 1000 foo objects

154

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Template Instantiation


It should be noted that these usages actually declare two types:




BBRS IP Software Group

struct __some_private_name1__ { int data[10]; int size; __some_private_name1__() { size = 10; } } struct __some_private_name2__ { foo data[1000]; int size; __some_private_name2__() { size = 1000; } }

The private names used are implementation-defined, but are consistent, in order to allow type-compatibility.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

155

Default Template Parameters




Template parameters can have default values just like function parameters:


template<class C, int i=10> struct myarray { C data[i]; int size; myarray() { size = i; }

BBRS IP Software Group

   

} myarray<int, 5> myarray<int> myarray<foo> myarray<foo, 1000>

a; b; c; d;

// 5 integers // 10 integers // 10 foo objects // 1000 foo objects

156

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

min() Example


Templates may be used to dynamically declare functions as well as types and classes. For example, the standard "minimum of two objects" may be implemented as a template function:


BBRS IP Software Group

template<class T> inline T min(T a, T b) { return ((a<b) ? a : b); }

157

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

min() Example


Usage of the template version of min() is just like the macro version:


int a, b; foo c, d; // Assume these are initialized if (min(a,b)) ...; if (min(c,d)) ...;

BBRS IP Software Group

Note that the type T is induced from the arguments, so there's no need to specify it. Each usage with different types ends up creating a new function. Memory is not wasted in this case because the template function is declared inline.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

158

Template Inheritance


Template classes may inherit from other classes (even other template classes). For example:


BBRS IP Software Group

template<class T> class List<T*> : public list_node { ... }; template<class T> class vector { ... }; template<class T> class Vec1 : public vector<void*> { ... }; template<class T> class Vec2 : public vector<T> { ... };

159

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Type Induction


In our min example:




template<class T> inline T min(T a, T b) { return ((a<b) ? a : b); }

BBRS IP Software Group

I pointed out that the type T is induced from the arguments. It is sufficient to write:


int a, b, c; a = min(b,c); a = min<int>(b,c);

You don't have to write:





160

Although you could if you wanted to


2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Type Induction


The compiler is able to induce the template parameter value from the function arguments. Since the template parameter is used as the type of the function parameters, it can simply use those parameter types as the template parameter's type. Of course, if the two parameters were different:


BBRS IP Software Group

long a, b; double c; a = min(b,c);

Then the type of the template parameter would be ambiguous and we would have to specify it.

161

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Type Induction


As an interesting curiosity, type induction can be used to implement a version of sizeof that returns the number of elements in an array (instead of the number of bytes):


BBRS IP Software Group

template <class T, int size> inline int arraySize( T(&)[size]) { return size; } int a[12]; foo j[37]; arraySize(a); // returns 12 arraySize(j); // returns 37;

 

162

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

More Information


Templates are a complicated part of C++. Entire books have been written describing all of the things that they are capable of. A lot of the standard C++ library is based on templates. See
http://dcserver/~dcharlap/sd99/advanced_cxx_templates.html

BBRS IP Software Group

for my lecture notes from the Software Development 1999 conference, regarding advanced uses of templates.

163

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction To C++
Namespaces
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Reason For Namespaces




When designing libraries, you declare a lot of public symbols. This includes functions, classes, enums, constants, etc. In order to prevent these symbols from conflicting with symbols in user code (or other modules of your library), it is typical to prefix symbols with library- or module-names. For example
  

BBRS IP Software Group

rsvpte_interface_t *rsvpte_mgr_create_intf... struct INTF *ospf_mgr_create_intf... ldp_err_t ldp_mgr_create_intf...

165

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Reason For Namespaces




This can quickly become difficult to maintain. Usage can also become annoying. Especially when your library name and module name are relatively long.

BBRS IP Software Group

166

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Namespace Example


Namespaces solve this. You declare a namespace and put your symbols in it. The symbols can be short and descriptive this way. For example:


BBRS IP Software Group

namespace ospf { struct INTF *mgr_create_intf(...); } namespace rsvpte { typedef ... interface_t; interface_t *mgr_create_intf(...); } namespace ldp { typedef ... err_t; err_t mgr_create_intf(...); }

167

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Namespace Example


To resolve a symbol in a namespace, use the :: operator:


 

struct INTF *i = ospf::mgr_create_intf(...); rsvpte::interface_t *i; i = rsvpte::mgr_create_intf(...); ldp::err_t e = ldp::mgr_create_intf(...);

BBRS IP Software Group

Of course, this doesn't look any better than simply sticking prefixes on the names. So additional features were defined to manage namespaces.

168

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Namespaces


A namespace is a scope. The usual scope rules apply to namespaces. This means that if a name is previously declared in the namespace or an enclosing scope, you don't need to explicitly name the namespace. For instance:


BBRS IP Software Group

rsvpte::interface_t *rsvpte::mgr_create_intf(...) { interface_t *i = new interface_t;

...
return i; }

169

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

using Declarations


BBRS IP Software Group

This works fine for simple cases, where a function sticks with symbols from its own namespace. But what if you frequently need symbols from another namespace? You can, of course, fully qualify the symbols but that can quickly become tedious and hard to read. Instead, you can use a using-declaration to alias a single symbol into the current scope:


void foo() { using rsvpte::interface_t; interface_t *i; i = mgr_create_intf(...); // error! }

170

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

using Directives


You can also alias an entire namespace into the current scope using a using-directive:


void foo() { using namespace rsvpte; interface_t *i; i = mgr_create_intf(...); } // OK

BBRS IP Software Group

171

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

using Directives


A using-directive at the global level effectively deletes the namespace. This should only be used as a transitional tool, while porting code over to a namespace. A using-directive inside a namespace definition can be used to create namespaces that are composites of other namespaces. A using-directive inside a function can be safely used as a notational convenience, since it won't have global impact on how code compiles.

BBRS IP Software Group

172

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Anonymous Namespaces


You can also declare an anonymous namespace. Without naming the namespace, its symbols can only be accessed from functions declared and defined within that namespace. For example:


BBRS IP Software Group

namespace { int a; void f() { /* do something */ } int g() { /* do something else */ } }

Obviously, there must be a way to call f and g if this is going to be useful.


2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

173

Anonymous Namespaces


BBRS IP Software Group

In order to make anonymous namespaces useful, every one has an implied using-directive at the end of it. In the previous example, f and g can be called from any code in the same translation unit as the namespace's definition. If the same anonymous namespace appears in a different translation unit, it will be a different namespace. In other words, anonymous namespaces can make functions private in the same fashion that the static keyword does in C. It should be obvious that anonymous namespaces in headers are pointless and will lead to much confusion.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

174

Namespaces


A function taking an argument of type T is usually defined in the same namespace as T. Because of this, if a function isn't found in the context where it is used, the compiler will look in the namespaces of its arguments. This saves a lot of typing, compared to explicit qualification, but doesn't pollute the namespace the way a using-directive can. Note that the namespace and the function must still be declared and in-scope for this to work.

BBRS IP Software Group

175

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Namespace Aliases


 

In order to avoid conflicts, using long, descriptive namespace names is a good idea. But long names are a pain to type in all the time. To resolve this problem, namespace aliases can be declared. For instance:


BBRS IP Software Group

namespace RCI_RSVP_TE_version_2 { struct interface_t; } namespace rsvp = RCI_RSVP_TE_version_2; rsvp::interface_t *i;

176

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Derived Namespaces


You can derive namespaces from other namespaces. For instance, we might declare each sub-module of RSVP in a separate namespace and then compose an overall RSVP namespace from the set of them:
 

BBRS IP Software Group

namespace rsvpte_mgr { ... } namespace rsvpte_parser { ... } namespace rsvpte_state_machine { ... } namespace { using using using } rsvpte namespace rsvpte_mgr; namespace rsvpte_parser; namespace rsvpte_state_machine;

177

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Namespace Selection


If you start combining namespaces with using-directives, you can wind up with name conflicts. You can, of course resolve these by specifying the namespace when the conflicting names are used, but this is ugly. Instead, you can use a using-declaration in the combined namespace. This will resolve the conflict by letting you specify which namespace to take the conflicting symbol from. This is known as namespace selection
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

178

Namespace Selection Example




For an example of selection, imagine a private String class (String is a standard library class) defined in a namespace. This namespace also contains two overrides for the plus operator, for concatenation:


BBRS IP Software Group

namespace Library_string { class String { ... }; String operator+(const const String operator+(const const }

String&, String&); String&, char *);

179

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Namespace Selection Example




This can get really messy. If I include the standard library's String class header at the same time, it will be ambiguous which class I end up using when declaring variables of type String. I can, however, declare my own namespace that selects the symbols I need from Library_string, and use that:


BBRS IP Software Group

namespace My_string { using Library_string::String; using Library_string::operator+; }

Note that this pulls all the overloads of operator+ into the namespace. They are all the same symbol.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

180

Namespace Selection Example




Using selection like this, any new methods on Library_string::String will be automatically pulled into the My_String namespace. Ditto for any new overloads of Library_string::operator+. Similarly, code using the My_String namespace will be alerted (through compiler errors) if one of the Library_string symbols should get deleted. If we didn't do this, but instead used a using-directive to pull Library_string's symbols into scope, deletion of those symbols would result in code silently using the public String class instead. Which is probably not what we want.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

181

Namespaces Are Open




Finally, it should be noted that namespaces are open. You can add symbols to a single namespace from many locations. For instance:


namespace A { int f(); } namespace A { int g(); }

BBRS IP Software Group

 

At this point, the namespace A contains both f and g. This allows us to distribute a namespace's definition across multiple header files, to make code easier to understand.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

182

Common Namespace Problem




When defining a function that's declared in a namespace, you should explicitly state the namespace to prevent errors. For example, with this namespace:


BBRS IP Software Group

namespace Mine { void f(); }

This typo will produce an error:




void Mine::ff() { ... }

183

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Common Namespace Problem




Had you instead written the function this way:




BBRS IP Software Group

namespace Mine { void ff() { ... } }

Then you would have accidentally defined a new symbol Mine::ff. The compiler won't have any way of knowing that the function name is actually a typo.

184

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

More Information


See
http://dcserver/~dcharlap/sd99/namespaces_and_their_impact_ on_cxx.html for my lecture notes from the Software

Development 1999 conference, regarding namespaces.

BBRS IP Software Group

185

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction To C++
Multiple Inheritance
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Multiple Inheritance


When defining a hierarchy of classes, sometimes a subclass logically should inherit from two or more base classes. C++ allows this multiple inheritance using standard language features.

BBRS IP Software Group

187

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Satellite Example


For example, consider a simulation environment where activity is represented by a class Task, and I/O is performed by a class Displayed. We can then define a class to represent a simulated object that both performs activity and I/O:


BBRS IP Software Group

class Satellite : public Task, public Displayed { ... }

188

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Satellite Example


The subclass inherits all data members and methods from both superclasses, in addition to anything it defines locally. For example:


BBRS IP Software Group

void f(Satellite &s) { s.draw(); // Displayed::draw() s.delay(10); // Task::delay() s.transmit(); // Satellite::transmit() }

189

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Satellite Example


Similarly, because Satellite inherits from both Task and Displayed, it can be passed to functions that expect either class:


void hilight(Displayed *); void suspend(Task *); void g(Satellite *p) { hilight(p); // gets a pointer to the Displayed part suspend(p); // gets a pointer to the Task part }

BBRS IP Software Group

190

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Satellite Example


Virtual functions work as usual. A function calling a virtual method of a Task or Display pointer will still call the override if passed a pointer to the Task or Display part of a Satellite.

BBRS IP Software Group

191

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Resolving Ambiguity


There is room for ambiguity, however. Two base classes may have member functions with the same name. For example:


BBRS IP Software Group

class Task { ... virtual debug_info *get_debug(); }; class Displayed { ... virtual debug_info *get_debug(); };

192

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Resolving Ambiguity


When the ambiguous method is called, the specific superclass used must be resolved:


void f(Satellite *sp) { debug_info *dip; dip = sp->get_debug(); // error - ambiguous

BBRS IP Software Group

dip = sp->Task::get_debug(); // OK dip = sp->Displayed::get_debug(); // OK }

193

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Resolving Ambiguity


Explicit resolution of the ambiguity is ugly, however. It is often better to merge the functionality using an override of the ambiguous function. For instance:


BBRS IP Software Group

debug_info *Satellite::get_debug() { debug_info *dip1, *dip2, *dip3; dip1 = dip2 = dip3 = return } Task::get_debug(); Displayed::get_debug(); dip1->merge(dip2); dip3;

194

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Resolving Ambiguity
By localizing the ambiguous resolution into a single function, it means subclasses of the multiply-inherited class don't have to worry about the ambiguity.  Subclasses can use whatever qualification they need to resolve calls. For instance:


BBRS IP Software Group

class Telstar : public Satellite { ... void draw () { draw(); // recursive Satellite::draw(); // finds Displayed::Draw Displayed::draw(); Satellite::Displayed::draw(); // redundant } };

195

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction To C++
Virtual Base Classes
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Common Base Class Example




Sometimes, your class may inherit from two classes that in turn inherit from a common base class. For example, we might define a class (Storable) for any object that can write/read itself to/from a file:


BBRS IP Software Group

class Storable { public: virtual const char *get_file() = 0; virtual void read() = 0; virtual void write() = 0; virtual ~Storable() { write(); } };

197

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Common Base Class Example




It is logical that all classes that want to be able to store themselves to disk would inherit from Storable:


BBRS IP Software Group

class Transmitter : public Storable { public: virtual void write(); ... }; class Receiver : public Storable { public: virtual void write(); ... };

198

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Common Base Class Example




Now, let's define a Radio class that consists of both a Transmitter and a Receiver:


BBRS IP Software Group

class Radio : public Transmitter, public Receiver { public: virtual const char *get_file(); virtual void read(); virtual void write(); ... };

Note that Radio has two Storable superclasses! One inherited from Transmitter and the other from Receiver.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

199

Common Base Class Example




Typically, an implementation would call the superclass methods and then call its own private implementation:


BBRS IP Software Group

void Radio::write() { Transmitter::write(); Receiver::write(); // radio-specific writing calls... }

This happens to work OK because the Storable class can be safely replicated. It contains no data and its write() method is pure virtual.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

200

Problem With Common Base Classes




But what happens if Storable declares data?




class Storable { const char *store; Storable (const Storable&); Storable& operator= (const Storable&); public: Storable(const char *s); ... };

BBRS IP Software Group

This is a problem! There are now two instances of the data in a Radio. Which one gets used? This ambiguity will lead to unpredictable behavior.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

201

Reason For Virtual Base Classes




What we need to do is to make sure that there is only one Storable component in a Radio. We do this by making the immediate subclasses of Storable virtually inherit from it:


BBRS IP Software Group

class Transmitter : public virtual Storable { ... }; class Receiver : public virtual Storable { ... };

202

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Virtual Base Classes




All classes that virtually inherit from the same base class will share the same instance of that base class if they are later combined using multiple inheritance. A class that does not virtually inherit from its base class will always get a unique copy of that base class if it is combined with a sibling class using multiple inheritance.

BBRS IP Software Group

203

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Problem With Virtual Base Class Example




Note that in implementations, you may have issues with the base class's methods being called multiple times for a single instance of a subclass. This is often incorrect behavior. Especially if the calls have side effects. For example, some classes for windows:
  

BBRS IP Software Group

Window - a plain window Window_with_border - a window with a border Window_with_menu - a window with a menu bar

204

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Problem With Virtual Base Class Example




Following the Radio example, these might be defined:




BBRS IP Software Group

205

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

class Window { public: virtual void draw(); // Draw the window }; class Window_with_border : public virtual Window { public: virtual void draw(); }; class Window_with_menu : public virtual Window { public: virtual void draw(); };

Problem With Virtual Base Class Example




The implementations for the subclasses can call the superclasses:




BBRS IP Software Group

void Window_with_border::draw() { Window::draw(); // draw a border } void Window_with_menu::draw() { Window::draw(); // draw a menu bar }

206

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Problem With Virtual Base Class Example




Now let's say that we are defining an application window that has both a border and a menu bar:


BBRS IP Software Group

class Clock : public Window_with_border, public Window_with_menu { public: virtual void draw(); };

207

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Problem With Virtual Base Class Example




But look at what happens if we do a simple implementation of the draw method:




BBRS IP Software Group

void Clock::draw() { Window_with_border::draw(); Window_with_menu::draw(); // draw clock stuff }

Notice that Window::draw is called twice! This will waste CPU time and may result in more serious problems, depending on its side effects.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

208

Avoiding Duplicate Base-Class Method Calls




The C++ language doesn't provide a solution for this, but the problem can be avoided through some clever organization of the classes. To do this, we declare separate non-virtual draw_self methods in all of the subclasses and have the draw methods call these. Then in the application class, we can explicitly call the base-class's draw method followed by the non-virtual draw_self methods that we need to call without worrying about the calls propagating to the base class.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

209

Avoiding Duplicate Base-Class Method Calls




class Window_with_border : public virtual Window { public: virtual void draw(); void draw_self(); }; class Window_with_menu : public virtual Window { public: virtual void draw(); void draw_self(); };

BBRS IP Software Group

210

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoiding Duplicate Base-Class Method Calls




void Window_with_border::draw() { Window::draw(); draw_self(); } void Window_with_menu::draw() { Window::draw() draw_self(); }

BBRS IP Software Group

211

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Avoiding Duplicate Base-Class Method Calls




Now our application's draw method can be written in a way that doesn't call Window::draw() more than once:


BBRS IP Software Group

void Clock::draw() { Window::draw(); Window_with_border::draw_self(); Window_with_menu::draw_self(); // draw clock stuff }

This can still cause some maintenance problems, but it solves the run-time problem.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

212

Multiple Inheritance


There is a lot more to multiple inheritance than just this. For more information, see section 15 of The C++ Programming Language

BBRS IP Software Group

213

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction To C++
Run-Time Type Identification (RTTI)
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

The Problem Solved By RTTI




Subclasses are only assignment compatible in one direction. You can assign a subclass pointer/reference to a base class pointer/reference, but not the other way around:


class a; class b : public a; a aObj; b bObj; a* aPtr = &bObj; // Perfectly legal b* bPtr1 = aPtr; // Illegal b* bPtr2 = &aObj; // Illegal

BBRS IP Software Group

  

215

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

The Problem Solved By RTTI




One solution is to simply apply a cast, but with some serious problems if it's not applied correctly:


BBRS IP Software Group

  

class a; class b : public a; a aObj; b bObj; a* aPtr = &bObj; b* bPtr1 = (b*)aPtr; // This works b* bPtr2 = (b*)&aObj; // This shouldn't have worked!

Casting also won't recompute pointer values, which is necessary when doing this with multiple inheritance.

216

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Dynamic Cast


The solution is the dynamic_cast operator. It examines the actual type of the data and will do the right thing:


BBRS IP Software Group

  

class a; class b : public a; a aObj; b bObj; a* aPtr = &bObj; b* bPtr1 = dynamic_cast<b*>(aPtr); b* bPtr2 = dynamic_cast<b*>(&aObj);

After executing this, bPtr1 will have the correct pointer value. bPtr2 will have NULL, since object aObj is incompatible with object class b.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

217

Dynamic Cast


dynamic_cast will return NULL whenever the object and class are not compatible with each other. When there are multiple ambiguous base classes (such as when using some extreme cases of multiple inheritance) it will also return NULL. The base class in a dynamic_cast must be polymorphic. That is, it must have at least one virtual method. Objects of non-polymorphic classes don't necessarily have the internal pointers to RTTI data that dynamic_cast requires in order to work.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

218

Dynamic Cast


dynamic_cast works on references as well as pointers. But because there is no such thing as a NULL reference, dynamic_cast will throw a bad_cast exception when the cast can not be performed.


BBRS IP Software Group

  

class a; class b : public a; a aObj; b bObj; a& aRef = bObj; b& bRef1 = dynamic_cast<b&>(aRef); b& bRef2 = dynamic_cast<b&>(aObj);

In this case, the last line throws a bad_cast exception.


2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

219

Dynamic Cast


When using dynamic_cast with references, it is a very good idea to use an exception handler.


 

BBRS IP Software Group

class a; class b : public a; a aObj; try { b& bRef = dynamic_cast<b&>(aObj); } catch(bad_cast) { // The cast failed. Do something about it. }

220

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Static vs. Dynamic Cast




static_cast does not check an object's RTTI data to see if the cast is possible, but it does do the pointer arithmetic needed for casting from one of several base classes in a multiple inheritance hierarchy (unlike a C-style cast). static_cast can not cast from a virtual base class in multiple inheritance, however, since this is impossible without referencing the RTTI class data that dynamic_cast uses (and static_cast does not use.)

BBRS IP Software Group

221

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Static vs. Dynamic Cast




So why should anybody ever use static_cast?




The pointer/reference may be to data that isn't polymorphic (like, from legacy C code) The data may be a (void *) - which by definition can not be assumed to point to anything specific and therefore has no accessible RTTI information. dynamic_cast has some overhead, although it's not much.

BBRS IP Software Group

222

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

The typeid Operator




dynamic_cast is usually useful enough for the situations where RTTI is required. But sometimes you need more specific information about a class. For that, there is the typeid operator. Like the sizeof operator, typeid may be applied to a type, an object, a pointer, a reference, or any other expression. typeid returns a reference to a type_info structure (defined in the <typeinfo> header) that identifies the type.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

223

The typeid Operator




For an example of typeid in action:




#include <typeinfo> void f(Shape &r, Shape *p) { type_info &ts, &tr, &tpp, &tp;

BBRS IP Software Group

ts = typeid(Shape); // Info about Shape tr = typeid(r); // Type of the reference tpp = typeid(*p); // Type of the data pointed to tp = typeid(p); // Type of the pointer itself }

224

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

The type_info Class




The portable parts of the type_info class look like:




class type_info { public: virtual ~type_info(); bool operator==(const type_info&) const; bool operator!=(const type_info&) const; bool before(const type_info&) const; const char *name() const; private: type_info(const type_info&); type_info& operator=(const type_info&); };

BBRS IP Software Group

225

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

The type_info Class




BBRS IP Software Group

 

The destructor (~type_info) is to ensure that the class is polymorphic. The == and != operators are used to test equality. It is important to use these on the type_info objects and not simply comparing the values of pointers to type_info objects, because there may be several type_info objects corresponding to a single type. The before() method is used for ordering the types (for sorting.) The order is arbitrary - it only has to be consistent within a single run of the program. The name() method retrieves the type's name The private copy constructors prevent accidental duplication of type_info objects.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

226

Avoid Abuse of RTTI




Most of the time, there will be no need for any RTTI (dynamic_cast or typeid). Virtual methods, inheritance, and templates provide most of the application-level features that many programmers try to gain through use of RTTI. There are, however, some legitimate situations where RTTI may be needed, so don't design an overly complicated solution simply to avoid using it.

BBRS IP Software Group

227

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

More Information


See http://dcserver/~dcharlap/sd99/rtti.html for my lecture notes from the Software Development 1999 conference, regarding run-time type indentification.

BBRS IP Software Group

228

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction to C++
Part 4: The Standard C++ Library
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

C-Compatible Library Components




All of the features of the C library are available in C++. You can simply include the same headers as before:


#include <stdio.h> printf("foobar\n");

BBRS IP Software Group

230

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

C-Compatible Library Components




A preferable way, however, is to use the new headers for these components. The header names are the same as before, but with a c prefix and without the .h suffix. The symbols declared in the new headers are all in the std namespace, in order to avoid collision with user code. For example:


BBRS IP Software Group

#include <cstdio> std::printf("foobar\n");

231

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

New Headers


Most of the new headers do not have any suffix on their filenames. For instance:


#include <iostream> #include <vector> #include <stack>

BBRS IP Software Group

All standard library symbols are in the std namespace, although some pre-standard compilers may not yet have put them where they belong.

232

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

I/O Streams


#include <iostream> I/O streams are a mechanism for objects to write their data out to text and binary streams, and to read their data back from those streams. In the simplest form, they may be used in place of the standard C library's printf/scanf functions. The standard streams cin, cout, and cerr (actually std::cin, std::cout and std::cerr) allow access to the standard I/O handles stdin, stdout, and stderr, respectively.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

233

I/O Streams


For an output example:




int x, y, z; printf("x is %d, y is %d, z is %d\n", x, y, z);

is equivalent to:


BBRS IP Software Group

int x, y, z; std::cout << "x is " << x << " y is " << y << " z is " << z << '\n';

(I won't be pedantically mentioning the std:: namespace in the rest of my examples. Assume they're there.)
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

234

I/O Streams


And for an input example:




int x, y, z; char buffer[10]; scanf("%d %d %d %10s", &x, &y, &z, buffer);

is equivalent to


BBRS IP Software Group

int x, y, z; char buffer[10]; cin >> x; cin >> y; cin >> z; cin.width(10); cin >> buffer;

235

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

I/O Streams


More complicated data can be read and written by overloading the << and >> operators. For example, one possible way to import and export JPEG image files might be:


BBRS IP Software Group

class jpeg { ... }; ostream& operator<<(ostream &s, const jpeg &j); istream& operator>>(istream &s, jpeg &j); jpeg j; cin >> j; cout << j;

236

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

I/O Streams


Formatting of stream input and output can be performed by calling various methods on stream objects. Streams that read and write files can be created using the ifstream and ofstream classes (in the <fstream> header.) Streams that read and write strings can be created using the istringstream and ostringstream classes (in the <sstream> header.)

BBRS IP Software Group

237

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction to C++
Containers
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Containers


Containers are object classes that are used to store other objects. There are different kinds of containers that store the objects in different ways, optimized for different access patterns:
 

BBRS IP Software Group

 

Sequences (vector, list, deque) Sequence adaptors (stack, queue, priority queue) Associative types (map, multimap, set, multiset) "Almost" containers (string, valarray, bitset, array)

239

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Containers


All containers are template classes. When instantiating a container, you specify the type of object you want it to contain. For example:
 

BBRS IP Software Group

vector<int> // A vector of integers list<float> // A list of floats deque<IpAddr> // A deque of IP addresses

240

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Iterators


All sequence-containers use iterators for traversal. There are several kinds of iterators:
   

BBRS IP Software Group

Output (Out) Input (In) Forward (For) Bi-directional (Bi) Random-Access (Ran)

241

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Iterators


Different kinds of iterators support different subsets of the following operations:


   

BBRS IP Software Group

Read Access Write Iteration Comparison

242

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Iterators


Iterator operations are performed via overloaded operators. Note that not all operators are applicable to all kinds of iterators:

Input

Output

Forward = *p ->

Bi-directional = *p -> *p = ++ -== !=

Random = *p -> [] *p = ++ -- + - += -= == != < > <= >=

BBRS IP Software Group

Read Access Write Iteration Compare

= *p -> *p = ++ == != ++

*p = ++ == !=

243

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Iterators


There is a lot more to say about iterators, but we won't discuss them here, since most of their use is in the context of the objects they iterate over (e.g. containers, strings, etc.) See chapter 19 of The C++ Programming Language for all the details.

BBRS IP Software Group

244

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector


A vector is the most basic kind of container. Other standard containers support most of vector's functionality. Some containers add additional functionality. Unless otherwise noted, all containers share vector's interface.

BBRS IP Software Group

245

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector


Defined in the <vector> header A vector is effectively a dynamically-resizing array. It has the following features:
   

BBRS IP Software Group

Iterators for traversing the list. The subscript operator may be used for random access Stack operations (push, pop, etc.) List operations (insert, delete, etc.)

246

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Types


Vector defines the following typedefs as a part of the template:


   

BBRS IP Software Group

 

 

value_type - the type of each element allocator_type - the type of the memory manager object size_type - the type used for indexing elements difference_type - the type resulting from subtracting two iterators iterator, const_iterator - for forward traversal reverse_iterator, const_reverse_iterator - for backward traversal pointer, const_pointer - pointer to an element reference, const_reference - reference to an element

247

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Types
These types allow member function to access the container without knowing anything about the actual objects.  For example, a template function to add up all the elements in a container:



BBRS IP Software Group

template<class C> typename C::value_type sum(const C& c) { typename C::value_type s = 0; typename C::const_iterator p = c.begin(); while (p != c.end()) { s += *p; // dereference an iterator to get the object ++p; // increment an iterator to go to the next object } return s; }

248

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Iterators


The following methods exist for creating iterators:


     

BBRS IP Software Group

 

iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const;


249

The begin() functions point to the first element in the (forward or reverse) sequence. The end() functions point to one element beyond the last element in the (forward or reverse) sequence. Vector's iterators are random-access
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Iterators


Note that a reverse_iterator is a distinct type from an iterator. They are not interchangeable. The base() method of a reverse iterator returns an iterator that points to a position one beyond the position pointed to by the reverse_iterator. For example, to find the last instance of a value, you might use:


BBRS IP Software Group

template<class C> typename C::iterator find_last( C& c, typename C::value_type v) { typename C::reverse_iterator ri = find(c.rbegin, c.rend(), v); if (ri == c.rend()) return c.end(); typename C::iterator i = ri.base; return --i; }

250

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Element Access




Vectors support random-access to elements:




Unchecked access:
 

reference operator[] (size_type n); const_reference operator[] (size_type n) const; reference at(size_type n); const_reference at(size_type n) const; reference front(); const_reference front() const; reference back(); const_reference back() const;

Checked access (may throw out_of_range exception):


 

BBRS IP Software Group

First element:
 

Last element:
 

251

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Construction and Destruction




There is a full set of constructors, destructors and copy operators:


 

template <class T, class A=allocator<T> > class vector { public: // Construct an empty vector explicit vector(const A& = A()); // Construct vector with n copies of val explicit vector(size_type n, const T& val = T(), const A& = A()); // Construct a range of objects (In is an input iterator type) template<class In> vector(In first, In last, const A& = A()); // Construct from another vector vector(const vector & x);

BBRS IP Software Group

252

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Construction and Destruction




// Destructor ~vector(); // Copy from a range of objects (In is an input iterator) template<class In> void assign(In first, In last); // Copy from n copies of val void assign(size_type n, const T& val); };

BBRS IP Software Group

253

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Stack Operations




The following stack operations exist:




Append to the end of the vector:




void push_back(const T& x);

Remove the last element in the vector:




BBRS IP Software Group

void pop_back();

Watch out for underflow - don't pop an empty vector. This results in undefined behavior and may corrupt memory:


vector<int> v; v.pop_back(); // Undefined behavior, v's state is now undefined v.push_back(7); // Undefined behavior - v's state was undefined

254

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector List Operations




The following list operations exist for vectors:




Insert object x before position pos:




iterator insert(iterator pos, const T& x);

Insert n copies of x before pos:




BBRS IP Software Group

iterator insert(iterator pos, size_type n, const T& x);

Insert a range of objects before pos (In is an input iterator):




template<class In> void insert(iterator pos, In first, In last);

255

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector List Operations




Delete the element at position pos:




iterator erase(iterator pos);

Delete a range of elements:




iterator erase(iterator first, iterator last);

BBRS IP Software Group

Delete all elements:




void clear();

256

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Addressing Vector Elements




List operations (among others) require iterators. How do you get an iterator from an index? For example, how do you delete a specific element from a vector?
 

BBRS IP Software Group

 

 

c.erase(c.begin() + 7); // OK, if c's iterator supports + c.erase(&c[7]); // Bad. assumes too much about vector's // implementation c.erase(c+7); // Bad. adding 7 to a container makes no sense c.erase(c.back()); // Bad. back() returns a reference, not // an iterator c.erase(c.rbegin()+2); // Bad. iterator and reverse-iterator // are different types c.erase(c.end()-2); // OK (deletes second-to-last element) c.erase((c.rbegin()+2).base()); // OK, but obscure

257

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Vector Size And Capacity




Get the number of elements stored in the vector




size_type size() const; bool empty() const; size_type max_size() const; void resize(size_type sz, T val=T()); size_type capacity() const;

Is the vector empty (has no elements)?




Get the maximum number of elements the vector can hold




BBRS IP Software Group

Set the size (initializing new elements to val)




Get the capacity (amount of memory currently allocated)




Make room for n elements (pre-allocation), throw a length_error exception if n > max_size()


void reserve(size_type n);

258

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

More Vector Methods




Swap two vectors (more efficient than actually copying them to do a swap):


void swap(vector &);

BBRS IP Software Group

 

The == operator is overloaded for comparing two vectors. They're equal if they have the same number of elements and matching pairs of elements all compare equal using their == operators. The < operator is overloaded for comparing two vectors, using lexicographic ordering. Similarly, there are overloads for !=, <=, > and >= These allow you to make a container of vectors
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

259

vector<bool>


There's a specialized version of the vector template, used for keeping a vector of Boolean values. All vector operations work normally on vector<bool>. Note that iterators for vector<bool> can not be implemented as pointers to the objects, since you can't take the address of a single bit. Your code should never assume any particular representation for any kind of iterator object.

BBRS IP Software Group

260

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Generic Container Issues




Do not assume anything about the internal representation of a container. The standard doesn't define any specific representation. If you assume any specifics, your code will break when ported to other platforms/compilers.

BBRS IP Software Group

261

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Generic Container Issues




Elements in a container are copies of the objects inserted. Elements therefore require a compatible assignment operator. For example:


BBRS IP Software Group

X& X::operator=(const X& a) { // copy all of a's members to this return *this; }

If it would be bad to make copies of objects, you can make a container of pointers instead. For example:


vector<foo *> vector<foo>

Instead of:


262

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Generic Container Issues




BBRS IP Software Group

Another reason for making a container of pointers instead of objects is in order to preserve polymorphic behavior. You can't store a derived object in a container of base objects, because the copy constructor won't copy the derived object's fields. When you retrieve the object, the original (derived) class data will be lost. You can, however, store a pointer to a derived object in a container of pointers to the base class. When you retrieve the object, virtual methods will continue to work as expected, and RTTI may be used to determine the specific subclass (if this is necessary).

263

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Generic Container Issues




BBRS IP Software Group

Associative containers require that their elements can be sorted. Some operations (like sort()) also require that elements can be sorted. By default, the < operator is used to define order. If this is not suitable, then an alternative mechanism (involving a user-provided compare function) may be used, as long as the compare function is transitive:
  

cmp(x,x) must be false If cmp(x,y) and cmp(y,z) then cmp(x,z) Defining equiv(x,y) to be !(cmp(x,y) || cmp(y,x)), If equiv(x,y) and equiv(y,z) then equiv(x,z)

264

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Generic Container Issues




Note that the < operator, when used on C-strings (char pointers) will compare pointer values, not strings. This may lead to unexpected behavior if a container of char pointers is made. A compare function or overload for the < operator that calls strcmp may be used to make this work. For example:


BBRS IP Software Group

struct Cstring_less { bool operator< (const char *p, const char *q) const { return strcmp(p,q) < 0; } }; map<char *, int, Cstring_less> m;

265

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

List


Defined in the <list> header A list is a sequence optimized for insertion and deletion. Its methods are similar to vector. There are no subscripting operators. The capacity() and reserve() methods do not exist. List iterators are bi-directional List may be thought of like a doubly-linked list, but you should not make any assumption about the implementation, because it may be something else.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

 

BBRS IP Software Group

266

List Splicing


List splicing allows you to move elements from one list to another without copying the objects. The following methods are provided for this:


Move all elements from x to before pos in this list without copying


BBRS IP Software Group

void splice(iterator pos, list& x);

Move *p from x to before pos in this list without copying




void splice(iterator pos, list& x, iterator p);

Move a range of objects from x to before pos in this list without copying


void splice(iterator pos, list& x, iterator first, iterator last);

267

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

List Sort and Merge




In addition to splicing, lists may be sorted, and sorted lists may be merged together with the following methods:


Sort a list


void sort(); template<class Cmp> void sort(Cmp);

Sort a list with a user-provided compare function




BBRS IP Software Group

Merge sorted lists




void merge(list&); template<class Cmp> void merge(list&, Cmp);

Merge sorted lists with a user-provided compare function




268

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

List Front Methods




All containers have the back() method to reference the last element, and the push_back()/pop_back() methods for stack operation. Lists provide similar methods for the front of the list:


Get a reference to the first element:


 

BBRS IP Software Group

reference front(); const_reference front() const;

Push a new element to the front of the list




void push_front(const T&);

Remove the first element




void pop_front();

269

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Other List Methods




Remove an element without an iterator




void remove(const T& val); template<class Pred> void remove_if(Pred p);

Remove all elements that match a given predicate




Remove consecutive duplicate objects from a list




BBRS IP Software Group

void unique();

Remove consecutive duplicate objects that match a given binary predicate




template<class Pred> void unique(Pred p);

Reverse the order of a list




void reverse();

270

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Deque


Defined in the <deque> header A deque is a double-ended queue. Push/pop operations at either end (front or back) are efficient (like a list) Subscript operations are efficient (like a vector) Insertions/deletions are inefficient (like a vector) Iterators are random-access

BBRS IP Software Group

 

271

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Sequence Adaptors


An adaptor is not a class, but is an interface to another class. There are three kinds of adaptors for the sequence templates:
 

BBRS IP Software Group

stack queue priority queue

Adaptors are made from other containers.

272

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Stack


Defined in the <stack> header An interface to its corresponding container (deque by default). Non-stack operations are eliminated. Only stack operations (push to one end, pop from the same end) exist. back(), push_back(), and pop_back() are renamed with more conventional names: top(), push() and pop()
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

273

Stack


Any container that implements back(), push_back() and pop_back() may be used in a stack. Examples:
 

BBRS IP Software Group

stack<char> s1; // made from deque stack<int, vector<int> > s2; // made from vector

You can initialize a stack with another container, but it may be expensive, since elements are copied:


vector<foo> foovec; stack <foo, vector<foo> > foostack(foovec);

274

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Queue


Defined in the <queue> header An interface to its corresponding container (deque by default) Non-queue operations are eliminated. Only queue operations (push to one end, pop from the other end) exist. push_back() and pop_front() are renamed with more conventional names: push() and pop().

BBRS IP Software Group

275

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Queue


Any container that implements front(), back(), push_back() and pop_front() can be used in a queue. Note that this excludes vector

BBRS IP Software Group

276

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Priority Queue


Defined in the <queue> header Like a queue, but elements are kept sorted. Unlike queue, top() retrieves the next-to-be-popped element, instead of front() By default, elements are compared with the < operator, and top() returns the largest element A comparison function may be provided if the < operator is not appropriate
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

277

Associative Containers
 

Associative containers store objects with key values. The different kinds of associative containers are:


 

BBRS IP Software Group

map - a sequence of key/value pairs, sorted by key. keys are unique multimap - keys not unique set - a sequence of keys only. keys are unique multiset - keys are not unique

Most associative container operations are implemented by map (much like most sequence operations are implemented by vector)

278

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map


Defined in the <map> header An associative container. Sometimes referred to as a dictionary. Similar in concept to our BBT trees. A map is a sequence of key/value pairs. Each key in a map is unique. Iterators are bi-directional. Elements are kept sorted by their keys

BBRS IP Software Group

279

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Types


Map defines the following typedefs as a part of the template:


   

BBRS IP Software Group

     

key_type - the type of a key mapped_type - the type of the data value_type - the type of each key/value pair key_compare - the comparison function allocator_type size_type difference_type reference, const_reference iterator, const_iterator reverse_iterator, const_reverse_iterator

280

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Iterators


Map's iterators work like sequence iterators, but the values they refer to are key/value pairs. Dereferencing a map iterator will return an object of the template class pair<key_type, mapped_type>. This object class is also the value_type definition In each pair, the first element is the key and the second element is the value.

BBRS IP Software Group

281

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Pairs
 

Defined in the <utility> header

The two elements are named first and second  The public part of the class definition is:


BBRS IP Software Group

template<class T1, class T2> struct std::pair { typedef T1 first_type; typedef T2 second_type; T1 first; T2 second; pair() : first(T1()), second(T2()) {} pair(const T1& x, const T2 &y) : first(x), second(y) {} template<class U, class V> pair(const pair<U,V>& p) : first(p.first), second(p.second) {} };

282

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Element Access




The subscript operator is used to retrieve items from a map:




mapped_type& operator[](const key_type& k);

BBRS IP Software Group

If the key is not found, an element is added to the map with default values For example:


map<string, int> m; int x = m["Henry"]; m["Harry"] = 7; int y = m["Harry"]; m["Henry"] = 9; m["Harry"] = 37;

// "Henry" is initialized to 0, x is 0 // "Harry" is initialized to 0, assigned 7 // y is 7 // "Henry" is now 9 // "Harry" is now 37

283

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Constructors


All the usual constructors are provided. In addition:




// Construct an empty map explicit map(const Cmp& = Cmp(), const A& = A()); // Construct from a list of key/value pairs (In is an input iterator) template <class In> map(In first, In last, const Cmp& = Cmp(), const A& = A()); // Construct from another map map(const map&); // Destructor ~map(); // Assignment operator overload map &operator= (const map&);

BBRS IP Software Group

284

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Compare Functions


  

By default, keys are compared using the < operator. A map can be created using other compare functions. For example:


Default comparison class (less<string>) map<string, int> m1; Use Nocase (to be constructed by map) instead map<string, int, Nocase> m2; Use String_cmp (to be constructed by map) instead map<string, int, String_cmp> m3; Use a String_cmp object that we constructed ourselves map<string, int, String_cmp> m4(String_cmp(literary));

BBRS IP Software Group

285

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Compare Functions




The functions used for comparing keys and values (which are key/value pairs) are publicly available. They are called key_comp() and value_comp(), respectively. This allows you to pass one map's comparison function to another. For example:


BBRS IP Software Group

void f(map<string, int>& m) { // mm compares keys with < map<string, int> mm; // mmm compares keys with whatever function m uses map<string, int> mmm(m.key_comp()); }

286

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Operations


Find the element (or first element, for multimap/multiset) with a given key:
 

iterator find(const key_type& k); const_iterator find(const key_type& k) const;

BBRS IP Software Group

Find the number of elements with a given key:




size_type count(const key_type& k) const;

287

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Operations


Find the first element with a key greater than or equal to a given key:
 

iterator lower_bound(const key_type& k); const_iterator lower_bound( const key_type& k) const;

BBRS IP Software Group

Find the first element with a key greater than a given key:
 

iterator upper_bound(const key_type& k); const_iterator upper_bound( const key_type& k) const;

288

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Operations


Find both the lower bound and upper bound together in a pair (lower bound is first, upper bound is second):


pair<iterator, iterator> equal_range(const key_type& k); pair<const_iterator, const_iterator> equal_range(const key_type& k) const;

BBRS IP Software Group

289

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map List Operations




Like with sequences, there are list-type operations:




Insert a key/value pair:




pair<iterator, bool> insert(const value_type &val);

BBRS IP Software Group

Insert at a given position (pos is a hint for optimization):




iterator insert(iterator pos, const value_type &val);

Insert elements from a sequence (In is an input iterator):




template<class In> void insert(In first, In last);

290

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map List Operations




Erase an element


void erase(iterator pos);

Erase all elements with a given key (return value is the number actually erased):


size_type erase(const key_type& k);

BBRS IP Software Group

Erase a range of elements:




void erase(iterator first, iterator last);

Erase all elements:




void clear();

291

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Miscellaneous Functions




Get the number of elements




size_type size() const;

Get the size of the largest possible map




size_type max_size() const;

BBRS IP Software Group

Returns true if the map is empty (has no elements)




bool empty() const;

292

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Map Miscellaneous Functions




Swap two maps:




void swap(map&);

Two maps may be compared using the ==, !=, <, >, <=, and >= operators Although these are not terribly useful, they allow us to make containers that contain maps

BBRS IP Software Group

293

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Multi-Map


A multimap can do everything a map can do. In addition, multiple elements may share the same key. There is no subscript operator in a multimap, since it is a meaningless concept. The insert() function returns an iterator instead of a pair:


BBRS IP Software Group

iterator insert(const value_type&);

294

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Multi-Map Example


map<char *, int, Cstring_less> m; m.insert(make_pair("x", 4)); m.insert(make_pair("x", 5)); // No effect // m["x"] == 4

BBRS IP Software Group

multimap<char *, int, Cstring_less> mm; mm.insert(make_pair("x", 4)); mm.insert(make_pair("x", 5)); // mm now holds both ("x",4) and ("x",5);

295

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Set


Defined in the <set> header A Set is like a map, except that no values are stored. You can only access keys. value_type is the same as key_type value_compare is the same as key_compare There is no subscript operator

BBRS IP Software Group

296

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Multi-Set


A multiset is a set that allows duplicate keys A multiset's interface is identical to set's, with one exception: The insert() function returns an iterator instead of a pair:


BBRS IP Software Group

iterator insert(const value_type&);

297

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Almost-Containers


There are four other kinds objects that are not containers, but share many similar properties to containers. These are:
  

BBRS IP Software Group

String Valarray Bitset Array

298

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

String


A string provides subscripting, random-access iterators and most of the same interface as a container, but it does not provide for a wide selection of types as elements. Strings are optimized for use as a string of characters and has methods that allow them to be used in places where string operations are traditionally used Strings have an associated "character traits" object which defines the properties of the character type that the string stores. This allows for use with non-standard character encodings.
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

BBRS IP Software Group

299

Basic String


Defined in the <string> header




BBRS IP Software Group

template<class Ch, class Tr=char_traits<Ch>, class A=allocator<Ch> > class std::basic_string { ... }

There are two convenience typedefs provided as well:


 

typedef basic_string<char> string; typedef basic_string<wchar_t> wstring;

300

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Differences Between String and Vector




Most of basic_string's functionality is the same as vector's. The similarities will not be discussed here. Some of the differences are:


The iterators are not range-checked There is no front() or back(). The subscript operator and length method must be used instead. For example:


BBRS IP Software Group

string foo; // (Assume this to be initialized) char first_char = foo[0]; char last_char = foo[foo.length()-1];

301

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

String Constructors


Construct an empty string




explicit basic_string(const A& a = A());

Construct from another string (or a subset of another string)




basic_string(const basic_string &s, size_type pos=0, size_type n=npos, const A& a = A()); basic_string(const Ch* p, const A& a = A()); basic_string(const Ch* p, size_type n, const A& a = A()); basic_string(size_type n, Ch c, const A& a = A());

BBRS IP Software Group

Construct from a C-string




Construct from a C substring




Construct initialized to a repeating character





302

Construct from an iterator (In is an input iterator)




template<class In> basic_string(In first, In last, 2004. The Copyright in this document belongs to Marconi and no part of const A& a = A()); this document should be used or copied without their prior written permission.

String Constructors


String destructor:


~basic_string();

"All characters" marker:




static const size_type npos;

BBRS IP Software Group

303

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

String Construction Examples


     

BBRS IP Software Group

  

string string string string string string string string string

s0; // Empty String s00 = ""; // Empty string s1 = 'a'; // Error - no conversion from char s2 = 7; // Error - no conversion from int s3(7); // Error - no one-argument constructor s4(7, 'a'); // Initialized to "aaaaaaa" s5 = "Foobie"; // OK s6 = s5; // OK s7(s5,2,3); // Initialized to "obi"

304

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

String Construction Examples




char *p = ...; // Some external C string string s8(p+7, 3); // Initialized to p[7], p[8] and p[9] string s9(p, 7, 3); // Same, but more expensively vector<char> v = ...; string s10(v.begin(), v.end());

BBRS IP Software Group

Note that this last example is very generic. The iteratorbased constructor will work on any iterator that dereferences to a type that can be converted to char.

305

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

String Manipulations


There are a very large number of methods for manipulating strings. They are far too numerous to list here, given the huge number of overloads that exist. The main categories of string manipulation methods are:
 

BBRS IP Software Group

     

Assignment Comparison Append Insert Concatenate Find Replace Substring

306

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

String Conversion To C-String




Return an array of characters:




const Ch* data() const; const Ch* c_str() const;

Return a null-terminated string of characters:




BBRS IP Software Group

Note that the string owns the returned values from data() and c_str(). Do not free the memory. Do not cache the pointers. The memory may become invalid after the next non-const method call on the string. Copy the characters to an array (does not append null!):


size_type copy(Ch *p, size_type n, size_type pos=0) const;

307

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

String I/O


The << method is overloaded to write a string to an output stream The >> method is overloaded to read a string from an input stream The getline method reads a line from an input stream (using a user-specified EOL character):


BBRS IP Software Group

template<class Ch, class Tr, Class A> basic_istream<Ch,Tr>& getline(basic_istream<Ch,Tr>&, basic_string<Ch,Tr,A>&, Ch eol);

If eol is not specified, '\n' is used

308

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

ValArray


A ValArray is a vector that is optimized for use in high speed numerical computing. The only standard container operations that are applicable to valarray are size (get the number of elements) and subscript (to read/write elements.) Because the goal of a valarray is to make vector math as fast as possible, other methods are implementation dependant, although there are typically many commonalities.

BBRS IP Software Group

309

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Typical ValArray Methods




Common valarray methods include:




v[i] *= arg for every element:




valarray& operator*=(const T& arg);

(similar overloads for /=, %=, += , -=, ^=, &=, |=, <<= and >>=) Sum up all elements:


BBRS IP Software Group

T sum() const; valarray shift(int i) const; valarray cshift(int i) const; valarray apply(T f(T)) const; valarray apply(T f(const T&)) const;

Shift/rotate (cyclic shift) all elements:


 

Apply a function to each element:


 

310

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Typical ValArray Methods




Unary operations on each element


   

valarray& valarray& valarray& valarray&

operator-() operator+() operator~() operator!()

const; const; const; const;

BBRS IP Software Group

There are many more things you can do with a ValArray, but we won't go into them because they are typically only useful for vector arithmetic (such as signal processing or games.)

311

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set


Defined in the <bitset> header Designed to replace bit-flag manipulation. bitset<N> is a fixed-size array of N bits A bitset differs from vector<bool> due to its fixed size A bitset differs from a set because it is indexed by integer instead of associative keys

BBRS IP Software Group

312

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set


Because you can't have a pointer to a bit, bitset defines a "reference to bit" class to be used where references are necessary:


BBRS IP Software Group

template<size_t N> class std::bitset { public: class reference { friend class bitset; reference(); public: ~reference(); reference& operator=(bool x); reference& operator=(const reference&); bool operator~(); operator bool() const; reference& flip(); }; };

313

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set


Bitsets differ from other container classes (mostly for historical reasons).
 

Using an invalid index throws an out_of_range exception There are no iterators

BBRS IP Software Group

Bit positions are numbered right-to-left. In other words the value of b[i] is pow(i,2). A bitset may be thought of as an N-bit binary number

314

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set Constructors


Default constructor (all bits zero)




bitset();

Initial bits derived from a value




bitset(unsigned long val);

BBRS IP Software Group

Initial bits from a string (characters '1' and '0')




template<class Ch, class Tr, class A> explicit bitset(const basic_string<Ch,Tr,A>& str, basic_string<Ch,Tr,A>::size_type pos = 0, basic_string<Ch,Tr,A>::size_type n = basic_string<Ch,Tr,A>::npos);

315

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set Construction Examples


     

BBRS IP Software Group

 

bitset<10> bitset<16> bitset<32> bitset<10> bitset<10> bitset<10> bitset<10> bitset<10>

b1; // All-zeros b2 = 0xaaaa; // 1010101010101010 b3 = 0xaaaa; // 00000000000000001010101010101010 b4("1010101010"); // 1010101010 b5("10110111011110", 4); // 0111011110 b6("10110111011110", 2, 8); // 0011011101 b7("n0g00d"); // invalid_argument exception b8 = "n0g00d"; // Err: no char* to bitset conversion

316

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set Manipulation


The following operators are used for accessing and manipulating the bits in a bitset:


Bit-access (subscript)


reference operator[](size_t pos); bitset& operator&=(const bitset& s); bitset& operator|=(const bitset& s); bitset& operator^=(const bitset& s); bitset& operator<<=(size_t n); bitset& operator>>=(size_t n);

Logical operators
  

BBRS IP Software Group

Logical shift
 

317

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set Manipulation


Set bits to 1
 

bitset& set(); // Set all bits to 1 bitset& set(size_t pos, int val=1); // b[pos]=val bitset& reset(); // Set all bits to 0 bitset& reset(size_t pos); // b[pos]=0 bitset& flip(); // Toggle all bits bitset& flip(size_t pos); // Toggle b[pos] bitset operator~() const; bitset operator<<(size_t n); bitset operator>>(size_t n);

Set bits to 0
 

BBRS IP Software Group

Toggle bits
 

Make complemented/shifted set


  

318

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set Operations


Make an unsigned long from the bitset (throws overflow_error exception if there are too many significant bits)


unsigned long to_ulong() const; template<class Ch, Class Tr, Class A> basic_string<Ch,Tr,A> to_string() const; size_t size() const; size_t count() const;

Make a string from the bitset




BBRS IP Software Group

Get the number of bits in the set




Get the number of bits that are set to 1




319

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Bit-Set Operations


Comparison
 

bool operator==(const bitset& s) const; bool operator!=(const bitset& s) const; bool test(size_t pos) const; bool any() const; bool none() const;

Is the bit at position pos set?




Are any bits set?




BBRS IP Software Group

Are all of the bits clear?




320

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Array


An array is the same in C++ as in C. There are subscripting operators for element access and random-access iterators (in the form of pointers). Arrays don't know their own size and do not have any of the usual member operations and types

BBRS IP Software Group

321

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Arrays


It is possible, however to create a template that wraps an array to allow basic container functionality. For example:


BBRS IP Software Group

template<class T, typedef typedef typedef const typedef typedef const

int max> struct c_array { T value_type; T* iterator; T* const_iterator; T& reference; T& const_reference;

T v[max]; operator T*() { return v; } reference operator[](size_t i) { return v[i]; } iterator begin() { return v; } iterator end() { return v+max; } ptrdiff_t size() const { return max; } ...
322
2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

};

Container Operation Summary




The following table summarizes the standard container operations:


Subscript [] vector list deque stack queue priority queue map multimap set multiset string array valarray bitset O(log(n)) O(log(n)) O(log(n))+ O(log(n))+ O(log(n))+ O(log(n))+ O(n)+ O(n)+ const+ const const List operations O(n)+ const O(n) Front operations const const const O(log(n)) Back (stack) operations const+ const const const+ const+ O(log(n)) Bi Bi Bi Bi Ran Ran Ran Iterators Ran Bi Ran

BBRS IP Software Group

const const const const

323

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction to C++
Numerics
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Vector Arithmetic


Vector arithmetic is performed using the valarray class, which we've previously discussed Section 22.4 of The C++ Programming Language explains in great detail the things that can be done with ValArrays and related classes

BBRS IP Software Group

325

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Complex


The standard C++ library also includes a complex arithmetic library. Defined in the <complex> header It is a template in order to allow complex numbers to be based on any scalar type (e.g. float, double, and long double)

BBRS IP Software Group

326

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

The Complex Class




template<class T> class std::complex { public: typedef T value_type; complex(const T& r = T(), const T& i = T()); template<class X> complex(const complex<X>&a);

BBRS IP Software Group

T real() const; T imag() const; complex<T>& operator=(const T&z); template<class X> complex<T>& operator=(const complex<X>&); // Similarly: +=, -=, *=, /= // ... };

327

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Complex Unary/Binary Operators




Binary operators


complex<T> operator+(const const complex<T> operator+(const complex<T> operator+(const

complex<T>&, complex<T>&); complex<T>&, const T&); T&, const complex<T>&);

Similar operators for -, *, /, == and !=

BBRS IP Software Group

Unary operators:


complex<T> operator+(const complex<T>&); complex<T> operator-(const complex<T>&);

328

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Complex Coordinate Functions




Retrieve Cartesian components:


 

T real(const complex<T>&); T imag(const complex<T>&); complex<T> conj(const complex<T>&); complex<T> polar(const T& rho, const T& theta); T abs(const complex<T>&); // rho T arg(const complex<T>&); // theta T norm(const complex<T>&); // square of abs()

Generate conjugate:


BBRS IP Software Group

Construct from polar coordinates:




Retrieve polar values:


  

329

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Complex Mathematical Functions




Trigonometric functions:
 

complex<T> sin(const complex<T>&); Similarly for sinh, sqrt, tan, tanh, cos, cosh, exp, log and log10

Exponentiation:
  

BBRS IP Software Group

complex<T> pow(const complex<T>&, int); complex<T> pow(const complex<T>&, const T&); complex<T> pow(const complex<T>&, const complex<T>&); complex<T> pow(const T&, const complex<T>&);

330

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Complex Stream I/O




Overloads for the << and >> operators exist to allow complex numbers to be read/written from/to iostreams. Complex numbers are written in the form (x,y) Complex numbers may be read as either x, (x), or (x,y)

BBRS IP Software Group

331

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Complex Specializations


Specialized templates exist for:


  

complex<float> complex<double> complex<long double>

BBRS IP Software Group

This is done to restrict/facilitate type conversion and to provide opportunities for optimized libraries.

332

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Generalized Numerics


The <numeric> header provides a few generalized algorithms that perform common operations on arbitrary numeric types:
  

BBRS IP Software Group

accumulate() inner_product() adjacent_difference() partial_sum()

333

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Accumulate


The accumulate algorithm produces the sum of all elements its given (via two input iterators):


BBRS IP Software Group

template<class In, class T> T accumulate(In first, In last, T init) { while(first != last) init = init + *first++; return init; }

334

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Accumulate


A more generalized version of accumulate also exists, that allows any binary operation to be performed on the set of elements:


BBRS IP Software Group

template<class In, class T, class BinOp> T accumulate(In first, In last, T init, BinOp op) { while(first != last) init = op(init, *first++); return init; }

335

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Inner Product


An inner product is the result of summing the product of two sequences. That is, items at identical indices are multiplied together, and all of these products are summed up together:


BBRS IP Software Group

template<class In, class In2, class T> T inner_product(In first, In last, In2 first2, T init) { while(first != last) init = init + (*first++ * *first2++); return init; }

336

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Inner Product


A more generalized version of inner_product also exists, that allows any two binary operations to be used in place of multiplication and addition:


BBRS IP Software Group

template<class In, class In2, class T, class BinOp, class BinOp2> T inner_product(In first, In last, In2 first2, T init, BinOp op, BinOp2 op2) { while(first != last) init = op(init, op2(*first++, *first2++)); return init; }

337

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Adjacent Difference


Given a sequence of input values, generate a sequence that consists of the differences between adjacent values. Input sequence {a,b,c,d,...} produces {a,b-a,c-b,d-c,...} A basic function and one that uses a generalized operation (instead of subtraction) are provided for this:


BBRS IP Software Group

template <class In, class Out> Out adjacent_difference(In first, In last, Out result); template <class In, class Out, class BinOp> Out adjacent_difference(In first, In last, Out result, BinOp op);

338

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Partial Sum


A partial sum allows us to compute the end result of a set of incremental changes. Input sequence {a,b,c,d,...} produces {a,a+b,a+b+c,a+b+c+d,...} A basic function and one that uses a generalized operation (instead of addition) are provided for this:


BBRS IP Software Group

template <class In, class Out> Out partial_sum(In first, In last, Out result); template <class In, class Out, class BinOp> Out partial_sum(In first, In last, Out result, BinOp op);

339

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

Introduction to C++
Conclusion
BBRS IP Software Group

2003. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

www.marconi.com

Conclusion


This presentation covers all of the basics There are more details not covered There is no substitute for experience What are you waiting for? Start writing some code!

BBRS IP Software Group

341

2004. The Copyright in this document belongs to Marconi and no part of this document should be used or copied without their prior written permission.

S-ar putea să vă placă și