C++ reference

From FreeGameDevWiki

Jump to: navigation, search

Today, C++ is probably the most used programming language, and has been ported in various dialects to virtually any modern computer system. One of the most powerful C++ compiler is provided by the GNU Compiler Collection, which is open source and therefore freely available, and will be used throughout this reference.

Contents

Introduction

In the evolution of C++, it has become a multipurpose programming language by extending C with modern features. In the early 1980's Bjarne Stroustrup, the inventor of C++, extended C by adding classes (called C with Classes), which was by and by extended by some more features to what is known as C++ today. Attempt to standardize this new language began a couple of years afterwards, and isn't finished yet. The current version ISO 14882 was approved in 1998, though a new standard known as C++0x is expected in 2009.

Features of C++

With the evolution of C++ from C, it was extended by new programming features, and has become a mix of different concepts for many purposes and paradigms, but in general C++ has four feature sets:

  • C Programming Language
  • Object-Oriented Programming
  • Generics
  • Standard Template Library

Hello, World!

This program simply prints "Hello, World!" on a display, but is a quick introduction to the syntax of a programming language, and a sanity check of the tool chain.

// Hello, World! in C++
#include <iostream>
using namespace std;
 
int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

The first line is a comment, because two slashes (//) make the compiler ignore the rest of a line.

C++'s basic functionality is limited to bare essentials, but its range can be expanded by including standard library headers. The directive #include<iostream> includes the standard library file iostream, which enables basic input & output. Generally, a preprocessor directive begins with a pound sign (#), is not a regular code, and will proceed before compiling.

Every elements of the standard library is declared in the namespace scope std. To access its functionality, using namespace std globally enables access to the namespace std in a source file. If this isn't done, every access to an element of a namespace must explicitly specify the scope.

Every C++ program needs the int main() function, as it es the program start. The curly braces ({}) enclose the main programs code block.

The effect of the statement cout << "Hello, World!" << endl is to output Hello, World! on the screen. The output is done by cout, which is declared in the standard file iostream within the namspace std, and must be included therefore. By shifting (<<), it streams the sequence of characters (enclosed by quotation marks ("")) to the output, and finishes with endl, which is also part of iostream and breaks to a new line.

The statement return 0 finishes the main function. The return value zero (0) indicates the operating system that the program ends without errors.

Notice that a semicolon (;) finishes every statement.

To make the program executable save the source code (here as hello.cpp), compile, and execute from command line by typing:

$ g++ -o hello hello.cpp
$ chmod +x hello
$ ./hello
Hello, World!

C Programming Language

Fundamental types

Fundamental types are usally very simple build-in data types. To a compiler, variables are placeholder for values in memory. The type of a variable indicates the compiler how to tread a variable and its assigned name. Here, the variable x of type int holds the value 65.

int x = 65;

Boolean

The type bool only knows the two states false (zero) and true (non zero). The compiler interprets the keyword true as 1.

bool a = true;       // a = true = 1
bool b = false;      // b = false = 0
bool c = -23;        // c = -23 = true
bool d = 0;          // d = 0 = false

Characters

Character variables represent a character of the character set, e.g. ASCII, and replaced by the according value of the character. Character literals are enclosed by apostrophe ( ' ' ).

Per definition the type char is the smallest fundamental type, and seize 1 byte with a range of typically 256 various characters. Unfortunately the range of values is not standardized, and compilers may interpret them different, either from 0 to 255 or -128 to 127 (g++ default). To put things right, the qualifier signed (-128 to 127) and unsigned (0 to 255) can be used.

char c = 'A';                // c = 'A' = 65
signed char sc = -128;       // -128 to 127
unsigned char uc = 255;      // 0 to 255

The type wchar_t is representative for wide characters to support bigger character sets and seize 4 bytes.

wchar_t w = 65536;           // -2147483648 to 2147483647

Integers

The compiler interprets variables of the type int as integer, and correspond their according value. The range of values depends on the number of used bytes, what isn't standardized and depends on the compiler. Also integers can have the qualifier unsigned or signed (default). In addition the qualifier short, long and long long determine the number of used bytes, still depends on the compiler. Integer numbers can have the suffixes U for unsigned and L for long. Integer literals can be utilized as decimal, octal, hexadecimal and character.

// range of values only for GNU g++
int                 i = -2147483648;                   // -2147483648 to 2147483647
short int           si = 32768;                        // -32768 to 32767
signed short int   ssi = -32768                        // -32768 to 32767
unsigned short int usi = 65535;                        // 0 to 65535
long int            li = -2147483648;                  // -2147483648 to 2147483647
long long int      lli = -9223372036854775808;         // -9223372036854775808 to 9223372036854775807
unsigned long int  uli = 4294967295UL;                 // 0 to 4294967295

Floating point numbers

The C++ compiler can use three types of floating point numbers as defined: float for single precision, double for double precision and long double for extended precision. Usually a floating point literal is of type double. Floating point numbers can have the suffixes f or F.

// typicial floating point number
// 1.23   .23   1.   1.0   1.2e10   1.23e-15
float        f = .5;            // 1.17549e-38 to 3.40282e+38
double       d = -1.5f;         // 2.22507e-308 to 1.79769e+308
long double ld = 1.2e-3F;       // 3.3621e-4932 to 1.18973e+4932

Void

The type void is something like a pseudo-type, and indicates the absence of information. Usually it only works as a part of more complex types.

void v;           // error: simple voids don't exist
void f();         // no return value for function f
void* pv;         // pointer to object of unknown type

Type size

The size of a type depends on the compiler, but per definition char is the smallest type and seize 1 byte. All other types are measured in comparison to char, and can be determined with sizeof(TYPE). Though it is guaranteed that char has at least 8 bits, short at least 16 bits, and long at least 32 bits.

1 == char <= short <= int <= long
1 <= bool <= long
char <= wchar_t <= long
float <= double <= long double
TYPE == signed TYPE == unsigned TYPE

Typically, the size of char is 1 byte and therefore 8 bits (though exceptions exist), and int is 4 byte. Detailed information can be obtained by the standard library file limits.

#include <iostream>
#include <limits>
 
int main() {
 
    std::cout << "int (" << numeric_limits<int>::max() << " - " << numeric_limits<int>::min() << ")" << std::endl;
    std::cout << "char is signed == " << numeric_limits<char>::is_signed << std::endl;
    return 0;
}

Constants

The concept of constants is to emphatically give a variable the status of immutability with its initialization. Usually, this improves readability to literals, and enables a variable to be read-only (e.g. for pointers and function parameters). To declare a type to be constant, the type is prefixed by the keyword const, and must be initialized. If the type of a constant is know at the time of compiling, the value will be used directly and eliminates memory allocation.

const double PI = 3.14159;

Compound types

Compound Types are more sophisticated data types, build from the fundamental types.

Enumerations

The type enum can have a self-defined number of values, and will be used similar to an integer constant afterwards. By naming an enum a new type will be created, and can be used as variable with equivalent enumerators. Also an enumerator can be assigned a value.

enum {OFF, ON};          // OFF = 0; ON = 1
int i = ON;              // i = 1;
 
enum color {RED = 2, YELLOW = 4, GREEN = 8};
color c = YELLOW;
//c = 3;                 // error: no conversion from int

Arrays

An array is a data chain with a fixed number of elements of a type. Indexing of elements starts from zero (0) to size-1. In addition C++ can handle multidimensional arrays. To initialize an array, a list defines the elements. If no array size is specified, it is equivalent to the number of elements. A list can't contain more elements than the specified array size, but omitted elements at the end become zero.

char c[] = { 'f', 'r', 'e', 'e'};        // array of 4 characters initialized with f, r, e, e
int field[3][4]                          // multidimensional array of 3 arrays of 4 integers
int i[10]                                // array of 10 integers
i[0] = 335

Pointers

Variables of type pointer hold memory addresses of a type, thus enables directly addressing the memory. By asterisking (*) a type it is defined as a pointer. The address of an object can be obtained with the address-of-operator (&). Probably the most important task of pointers is dereferencing (readout values of the object pointed to), and happens by the dereferencing-operator (prefix *). As no object can allocate the address 0 (zero), a pointer with the value 0 (zero pointer) indicates not to refer to an object.

char c = 'X';
char* pi = &c;            // p holds the address of i
cout << *pi << endl;      // outputs 'X'
 
int** ppi;                // pointer to pointer to int
int (*fp)(int*);          // pointer to function taking a int* and returns an int
int* f(int*);             // function taking an int* and returns a pointer to int

If the use of pointers into array did not explicitly specify the array element, an implicit conversion from array to pointer is performed, and leads to the loss of information about its length; if needed, it must be stated in addition. Arrays as function arguments automatically perform this conversion, and never get copied.

char str[] = "FREEGAMEDEV";
char* p1 = str;            // pointer to str[0] (implicit conversion)
char* p2 = &str[13];       // pointer outside of array range (undefined compiler behavior)

Using constant on pointers also affects the referred object. Either the pointer, the object, or both get constant. The declarator operator (*const) declares the pointer being constant, while declaring the object being constant its type must be declared with const. The address of a constant can't be assigned non-restrictive pointer, to prevent its value being modified.

int *const p1;             // const pointer to int
char const* p2;            // pointer to const char
const char* p3;            // pointer to const char
const char *const p4;      // const pointer to const char

Pointers to void (void*) can point to any type, though it's not save, and mainly used in low-level programming on functions of unknown type. Using such an object needs explicit type casting. Pointers to functions and members can't be referred to a void*.

C-style strings

A string literals is a sequence of characters enclosed by quotation marks (""), and is an array of the number of its constant characters, appending a null character (\0) at the end. Per C definition, it is possible to assign a string to a char* (pointer), but can't be modified by a pointer. Only array strings can be modified. Strings are static, and identical string literals in arrays locate the same address in memory.

char str[] = "Free Game Development";          // actually "Free Game Development\0"
char* pstr = "Free Game Development";

References

A reference is an alternative alias name for an object, and acts as a permanent dereferencing constant pointer. To declare an object as reference its type is provided with the reference-operator (&), and must be initialized by a variable in addition.

int  i = 8;
int& j = i;       // j refers to i
j = 3;            // i = j = 3

Structures

A structure is composition of various elements and defines a new type. Declaring a variable of it is done as any other, and initialized by a list. access an element, the dot-operator (.) needs to connect the variable with the element. Another common way to access elements of a structure is by pointers, and done with the structure-pointer-dereference-operator (->) to connect. Notice the semicolon (;) at the end of the curly braces ({}).

struct developer {                             // define new struct type developer
    char* nick;
    int age;
};
 
developer jim = { "JimBob", 4 };               // initialization
 
jim.age = 54;                                  // accessing element of struct
 
developer* p;                                  // access via pointer
cout << p->name << ":" << p->age << endl;

Declarations & definitions

The names of a C++ identifier consists of letters (A-Z, a-z), numbers(0-9), or underscore (_). C++ is case sensitive, the first character must be a letter or underscore, and the compiler has no limitation for the length of a name, but linkers may have. Keywords can't be used as identifier.

Before an identifier (e.g. names of variables) can be used it must be declared, what means to assign a type to the name, so that the compiler knows how to deal with it. Often a declaration is also a definition, and defines the unit of the referring name. A C++ program can only have one definition for an identifier, but multiple declaration when they are of the same type. A declaration consist of an optional specifier, base type, declarator, and an optional initializer. If no initializer is specified its value is zero (0), but objects created in free memory don't. Multiple names can be declared in a list, when separated by comma (,). With typedef an new name will be declared for a type.

char c;                        // declaration of c as char
const int MAX = 4096;          // declaration and definition of MAX
extern double d;               // declaring d to be defined elsewhere
int x, y, z;                   // declaring multiple names
typedef short int int16;       // declare new type int16
int16 i = 0xff;

An object is an allocated range in memory, which is assigned a name. It is created with its definition and destroyed by leaving scope. An lvalue is an expression referring to an object.

Operators

The purpose of operators is to be a short-hand function for frequently used operations, e.g. in 2 + 3 the plus-sign (+) acts is the operator which applies an addition to its both operands 2 & 3. Notably, C++ differs operators between unary (one operand) and binary (two operands).

Arithmetic Operators

To the nature of a computer, C++ of course offers basic arithmetics (plus, minus, multiply, divide, remainder). These arithmetics can be proceed with their according arithmetic operators +, -, *, /, %. Notice, as these operators use two operands it's binary arithmetic. For an equation (=) the result is on the left side, while the calculation is on the right.

cout << 95 + 2;		// 6 = 9 – 5 + 2
cout << 8 * 4 / 2;		// 16 = 8 * 4 / 2
cout << 10 % 3;		// 1 = 10 % 3

Beside of binary arithmetic, C++ offers also the unary arithmetic operators +=, -=, *=, /=, %= to shorten arithmetics with self-assignment. So an equation as i = i + 5 can be shortened to i += 5 what is exactly the same. Furthermore, an incrementation or decrementation by one (1), which would usually look as i = i + 1, or i += 1 then, can be shortened to either i++ (postfix) or ++i (prefix). The difference between prefix and postfix is, that prefix first performs the operation of an operand before it's value is being used, while at postfix the value is used first and afterwards operated.

int i = 20 + 5;		// i = 20 + 5 = 25
i += 5;			// i += 5 = i + 5 = 20 + 5 = 30
cout << --i;			// i = i - 1; outputs 29
cout << i++;			// outputs 29; i = i + 1 = 30
cout << i;			// outputs i = 30

Relational Operators

Relational operators compare their two operands, resulting in a boolean state of either true or false. The typical relational operators known by the C++ compiler are: less, less or equal, greater, greater or equal, equal, not equal (<, <=, >, >=, ==, !=).

6 < 9;				// true: 6 is less than 9
9 <= 5;			// false: 9 is less or equal 5
'z' > 'a';			// true: z is greater than a
int i = 5 >= 5;		// true: 5 is greater or equal 5
i == false;			// false: i is equal false
false != true;			// true: false is not equal true

Be aware not to exchange the relational operator == with the assignment-operator =.

Logical Operators

Just like relational operators, logical operators compare their operands, resulting as boolean either true or false. The C++ compiler knows the binary logical operators && (AND) and || (OR), usually used to connect relational operations, wich are either true if both operands are true, or if at least one of both operands is true. The unary logical operator ! (NOT) is only true if its operand is false.

true && true;			// true as both are true
false || true;			// true as the 2nd operand is true
bool b = false;
b = !b;			// b = true = not false

Bitwise Logical Operators

Unlike logical operators, bitwise logical operators &, |, ^, ~, >>, and << don't return a boolean state of either true or false, but a bit manipulated integer.

The binary bitwise logical operator & can be read as AND, | as inclusive OR, ^ as exclusive OR, and the unary operator ~ as NOT. As result they return a new integer by comparing each individual bit of the operands according to their logic. AND sets a bit if both operands have set that bit, OR if at least one operand, and XOR if only one operand. NOT returns the complement by reverting any bit. In practice, this is used with a bitmask, which signifies which bits to manipulate – AND to delete a specific bit, OR to set, and XOR to invert.

const int BITMASK = 0x20;	// 00100000
char c = 'a';			// 01100001
c = c ^ BITMASK;		// 01000001
c = c | BITMASK;		// 01100001
c = c & BITMASK;		// 00100000
c = ~c;			// 11011111

The bitwise shifts >> and << shift the bitpattern of an operand the a certain number of bit positions, either to the left or to the right, and 0 bits or sign bits are padded. Exceeding bits are dropped. In practice, shifting is done for efficient multiplication and division with the power of 2^n.

5 << 3;		// 5 * 2^3 = 40
5 >> 1;		// 5 / 2^1 = 2

Similar as with arithmetic operands, also bitwise logical operands have compound operators for self-assignment: &=, |=, ^=, >>=, and <<=.

Free Store

The lifetime of objects is usually limited to their scope. To be independent to the scope, what can be important for an algorithm, C++ offers the operators new to manually allocate an object on the free store (also known to be heap objects, or allocated in dynamic memory), and delete to manually release that object, and free the memory for other objects. As these operators work on individual objects, memory allocation for array's is done with the operators new [ ] and delete [ ]. In contrast to static object, dynamic object also hold the object's size, usually in a word.

char* ch;
ch = new char;				// allocates char on the heap and assigns its address to ch
*ch = 'X';				// access the heap object by pointer
delete ch;				// deallocates ch
 
struct Point { int x, y, z; };
Point* p = new Point[5];		// allocates and assigns dynamic memory
(p+3)->x = 5;				// access 3rd array element via pointer and select member x
delete p;				// deallocates p

Of course physical memory is limited in space, and if new unsuccessfully tried to allocate space, a specified function defined by set_new_handler() declared in <new> is called, which handles memory exhaustion and throws a bad_alloc exception.

#include <iostream>
using namespace std;
 
void out_of_space() {
	cerr << "Operator 'new' failed: out of space\n";
	throw bad_alloc();
}
 
int main() {
	set_new_handler(out_of_space);
	try { for (;;) new char[1000000]; }
	catch (bad_alloc) { cerr << "Memory exhausted!\n"; }
}

Type Conversion

Though it's better avoided, sometimes different types have to exchange their values and must be converted, also known as casting. In some cases the type of an object, e.g. raw data in memory, is simply unknown to the compiler, and also needs a cast to even work. In the type hierarchy a conversion happens implicitly for an upcast, but must be explicitly specified for downcasting. The operator static_cast converts between related types (some type-checking), reinterpret_cast between unrelated types (the least safest cast), and const_cast manipulates the const or volatile qualifier, which should only be used on parameters when it's certain a function input forgot to specify as const but doesn't modify. A less specific but more error prone conversion is inherited from C with the operator ( type ) expression which interprets any of those conversions. In addition, the dynamic_cast operator can check in run-time if a cast is safe or not, but only works on pointers or references to classes.

const char c = 'Z', *pc = &c;
int* pi = reinterpret_cast<int*>(const_cast<char>(pc);		// remove const and reinterpret as int
*pi = 'A';							// *pi & *pc = 'A'; but c = 'Z' as it is read-only
const_cast<char&>(c) = 'A';					// again, c = 'Z'
 
int i = static_cast<double>(100 / 80);
i = (double) 2.3;						// C-style cast

Statements

Simply said, statements are the control mechanism of a C++ program.

If-Else Selections

The if-else selection check a condition, and only executes a statement (or a larger code block) if its nonzero (true), optionally with an alternate else path. Though most conditional are constructs of logical and relational operators, any arithmetics work.

bool b = false;
if (b == true) cout << "TRUE" << endl;
else cout << "FALSE" << endl;

C++ also offers the conditional-statement shorthand, which can replace some if-else statements. The condition finishes with a question mark, followed by two statements, separated into if on the left and else on the right side of a double point.

int i = 10;
int res = (i <= 20) ? i : 0;		// res = i

Switch Selections

Often an if-else statement is used to check an object against a couple of values. Though this can be expressed with if-else­-if­ chains, C++ has the switch statement for this purpose. The switch questions the value of an object for a specific case, and proceeds the optional default if none fits. Keep attention not to forget terminating (usually) with break at the end of a case, otherwise switch continues executing the next case.

char c = 'Z';
switch (c) {
case 'A':
	cout << "first case" << endl;
	break;
// other switches in between, but now ...
case 'Z':
	cout << "last case" << endl;
	break;
default:
	cout << "default switch" << endl;
	break;
}

While Loops

The while statement is a preconditional loop, which checks a condition, and repeats that statement until the condition becomes false.

bool done = false;
while (!done) cout << "looping\n" << endl;	// loops endlessly

Do Loops

The do statement is similar to while, but checks its condition after the statement is executed, and repeats unless the condition becomes false.

bool done = false;
do cout << "looping\n" << endl;
while (!done);				// loops endlessly

For Loops

The for statement is actually a special counting loop, and repeats a statement a certain number of times, until that number has reached. The conditions of a for loop can have three parts: an initializer, the condition, and an expression.

for (int i = 0; i < 100; i++)
	cout << i << ". loop " << endl;

Break

A break statement can only be executed in a loop or switch, and immediately terminates it by leaving and continues with the next statement followed by the loop or switch:

while (true) break;		// breaks the endless loop

Continue

Similar as break, a continue statement immediately skips the rest of a loop, and retests the condition.

while (true) continue;	// continues forever

Goto

Though it's usually avoided, C++ also provides jumps with goto to a label, and may appear in real-time applications to efficiently leave a loop, or by computer generated code.

for (int i = 0; i < 1000; i++)
	if (i == 23) goto label1;
label1:

Macros

The concept of macros, a shorthand to replace a larger and frequently used piece of code when its called, has turned out to be error prone, and therefore a minor role in C++. Macros can't be overloaded, and are not recursive:

#define PI 3.1415926
double d = PI		// d = 3.1415926
 
#define ABS(X) (X >= 0 ? X : -X)
cout << ABS(23) << endl;           // cout << (23 >= 0 ? 23 : -23) << endl;

Though macros are usually a signal of bad design, in C++ it is almost impossible to avoid the use of the conditional macro definitions #ifdef, #ifndef, and #endif for certain purposes.

Scoping

A scope is a certain range in a program, which specifies the ranges of validity for names.

Local & Global Scope

A declaration of a name also assigns it to a scope, which is either local or global. By declaring a name inside of a block of curly braces ({}) it becomes local, and is accessible only within the code block of its declaration. By declaring a name outside of any blocks it becomes global, and can be accessed within the complete file. A declaration can obliterate a declaration of the same name from an enclosing scope, and be declared for the inner block again. The resolution operator (::) enables to still access global names.

int i;			// declare global i
 
int main() {
	int i;		// declare local i
	::i = 7;	// global i = 7
	i = 9;		// local i = 9
}

Storage Class Specifier

The scope of a variable also determines its lifetime. Normally, local object are created with their appearance inside a code block, and their lifetime automatically end when leaving. Though C++ offers the storage class specifier auto to specify that behavior, it's not necessary as it's default.

auto int i;		// same as: int i;

The keyword static specifies that a variable is created only once in a program, and continuous holds its value, even when leaving and re-entering scope.

void f() {
	static int i = 0;
	i++;			// i increases with every function call by 1
}

The keyword extern specifies a variable to be declared in a different file, and is usually used to declare variables of global scope. When declaring a variable with extern, the compiler does not allocate memory for it.

extern char c;		// c is declared in another file


The keyword registerspecifies the compiler to use the CPU registers to store its value. Though registers are the fastest storage they are very limited, depending on the hardware, and if no register is available auto is used instead.

register int i = 0;	// store i in CPU register

Namespaces

A namespace is a scope to express a logical group. Practically, if some declarations can be grouped, a namespace can express their connectivity. A member of a namespace can be accessed with the scope-resolution-operator (::) in the notation namespace-name::member-name from an outer scope, and also be defined outside.

namespace Lib {	int i; }		// namespace + declarations
Lib::i = 23;				// accessing namespace member

Because namespaces are open, member declaration can be placed in different code fragments:

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

Another reasonable use of namespaces is to avoid name clashes - name ambiguities in code, especially for larger projects with multiple developers. For protection against name clashes, it also makes sense to point out a local logical group by using a nameless namespace.

namespace { }

In C++, it is also possible to give namespace an alias, what can be handy to shorten longer names or version updating.

namespace FGD = Free_Game_Development;
namespace Lib = FreeGameDev_Library_v1_0;

Sometimes, it seems tedious to specify a namespace when accessing a member, especially if frequently used. Therefore, C++ offers the using declaration to make a specified member accessible, or the using directive for an entire namespaces. In general, the global use of using should only be used in transient situations, and better be avoided otherwise - nonetheless, local use safe. Withing a namespace, using is can be used as tool for composition.

using namespace-name::member;
member();
 
using namespace namespace-name;
another_member_of_namespace_name();

Because the legality of coexisting multiple namespace definitions, the creation of interfaces with separated definitions is possible. Principally, interfaces are used to minimizes the dependency of declaration and definition. It is possible to compose an interface of existing namespaces, or by selection explicit members. Also overloading between namespaces works, and is required to minimize code changes for existing libraries.

Program Structure

In software development, it has been established to cope with the complexity of larger programs by braking down the sources into smaller logical modules of source files. Before the actual compilation, the various source files are preprocessed into translation units. At last, the linker merges all units into the final program.

Programs & Termination

A program is the binary form of compiled source code. In C++, any program starts by calling the int main() function, and ends by leaving main(), calling exit(), calling abort(), throwing an uncaught exception, or an erroneous program crash.

With the return value of zero (0) a program indicates to the underlying operating system a successful termination, as it's also done withexit().

The function atexit() can also execute code with the program termination, but there's only has a limited number of atexit functions, indicated with a return value of zero (0)

void my_cleanup();
 
void somewhere() {
	if (atexit(&my_cleanup) == 0) { /* ... */ } 		// my_cleanup will be called at normal termination
	else { /* ... */ }					// oops: too many atexit functions
}

Header Files

In C++, the file suffix *.h indicates a header file, and usually is like an interface with declarations only, to ensure being identical when included into different translation units.

Headers can be included with the preprocessor directive #include into a translation unit. Thereby, the compiler distinguishes between #include <...> to include headers of the standard library (e.g. iostream, cstudio), and #include "..." to include headers relative to the current location:

#include <iostream>		// include from standard directory
#include "my_header.h"		// include from local directory

To eliminate the risk of multiple inclusion, include guards are used to check if a header is already included:

// header.h
#ifndef HEADER_H
#define HEADER_H
 
/* header declarations here */
 
#endif	// HEADER_H

The common strategy (for smaller projects) is to have a corresponding *.c file (or *.C, *.cxx, *.cpp, *.cc) for the definitions of a header. Nonetheless, larger projects may have one file the definitions, but multiple header files: the user interface as *.h only with user functions, and another implementation interface with all logical declarations of the logical units; any definitions is in *.c - sometimes, larger projects provide a simple average user interface for the casual user, and and expert user interface which covers all functions.

// gamelib.h - user interface
namespace GameLib {
	bool running(bool status);
}
 
// gamelib_impl.h - implementations interface
#include "gamelib.h"
namespace GameLib {
	bool running(bool status);
	void render(const char* list);
	// ...
}
 
// gamelib.c - Definition File
#include "gamelib_impl.h"
bool GameLib::running(bool status) { }
void GameLib::render(const char* list) { }

Linkage to C

Often a program is developed in various languages. Thereby, linkage conventions within the extern declaration can help to minimize complications between different languages and compilers. The extern "C" directive explicitly specifies C conventions.

extern "C" char* cf(char*, const char*);
extern "C" {
	char* foo(const char*, const char*);
	int bar(const char*);
	// ...
}

With the use of conditional compilation, a linkage block can easily modified, and compile C as well as C++ headers:

#ifdef __cplusplus
extern "C" {
#endif
 
	char* foo(char*, const char*);
	int bar(const char*, const char*);
	// ...
 
#ifdef __cplusplus
}
#endif

Object-Oriented Programming

Object-oriented programming (OOP) is an on the concept of object-orientation based programming paradigm. The basic idea of object-orientation is to categorize processing data according to their attributes + methods into classes, and to create objects of them to better match real-world models with advanced flexibility and reusability. For that purpose, the fundamental tools of object-orientation are:

  • Encapsulation - Hiding information about the data structure, and provide access only via a public interface.
  • Inheritance - Hierarchically creating new derived classes of an existing base class.
  • Polymorphism - Accompanied with inheritance, interchangeability of objects sharing the same interface.

For better understanding of object-orientation it is recommended to study design patterns + refactoring, and beside of OOP also object-oriented analysis & design (OOA/D).

Classes

The concept of classes is to provide a tool for the creation of new types, which can be used like build-in types. Similar as a struct, a class defines a collection of data, representing a concept with a set of member functions for manipulation. Notice the semicolon at the end of the class definition:

class ClassName {
	int attribute;		// attributes = variables
	void method();		// methods = functions
};

As different classes can have members with identical names, it is required to explicitly specify the class when defining a member from outside of the class's scope:

void ClassName::method() { /* ... */ }		// method definition

The creation of a class object, an instance, is just like any other type, while accessing it's members is similar to a structure:

ClassName object;
object.member;
object->member;

Access Control

Access control is actually what makes the difference to a class from a struct. Members of a class can either be:

  • private - only accessible from within the class by member functions (default)
  • public - accessible by class objects; the public interface
  • protected - accessible from within the class and friends
class ClassName {
private:
	int money;
protected:
	int beer;
public:
	int love;
};

Appendix

Useful tools

For debugging

  • gdb - debugger
  • valgrind - to find memory related problems
  • ltrace - traces library calls
  • strace - traces system calls (ex: reacts if opening a file fails)
  • ddd - visual debug program that lets you step through you code like VC++ but on linux ( sudo apt-get install ddd )

For merging

  • Meld - Diff and merge tool

Development tools

References

Links

Books

  • The C++ Programming Language (ISBN 978-0201700732) by Bjarne Stroustrup - Addisson Wesley (2000)
  • Effective C++: 55 Specific Ways to Improve Your Programs and Designs, 3rd Edition (ISBN 978-0321334879) by Scott Meyers - Addison Wesley (2005)
  • More Effective C++: 35 New Ways to Improve Your Programs and Designs (ISBN 978-0201633719) by Scott Meyers - Addison Wesley (1995)
  • Thinking in C++, Volume 1: Introduction to Standard C++ (ISBN 0-13-979809-9) by Bruce Eckel - Prentice Hall (2000) - free online-book
  • Thinking in C++, Volume 2: Practical Programming (ISBN 0-13-035313-2) by Bruce Eckel - Prentice Hall (2003) - free online-book
Personal tools