|
Guillaume Caumon,
Fev 2000, revised Jan 2001 and Jan 2005.
.
A C++ code is defined by objects and functions operating on
these objects.
The description and the implementation of these objects and functions
are separated physically:
- descriptions are located in header files (.h);
- implementations lies in .cpp or .cc files (source files).
Typically, a header file can be included in another header or in a source file
using:
#include <my_description.h>
This #include directive tells the preprocessor to copy the contents
of my_description.h at the current location. This means that
any object or function declared in my_description.h can be used
after this line.
The compilation itself acts on a source file, creating an object file (.o).
All object files in a program can be combined at link-time to
create a library (.lib or .dll or .so) or an executable (.exe).
| C |
C++ |
/* this is a multi-line
C comment */
|
/* this is also a multi-line
C++ comment... */
// and this is a single-line C++ comment
|
Rules:
- Use a consistent convention for formatting comments.
- Use a tool to generate interactive documentation from the header
files (more details and example here).
A C++ function should have a prototype,
declaring its arguments and return type.
Default arguments can be specified in the prototype :
// prototype description (in a header file .h)
void function( int n, int k = 0 );
// implementation (in a source file .cc or .cpp)
void function( int n, int k ) {
// function body
}
results:
function( 1, 3 ); // n = 1, k = 3
function( 10 ); // n = 10, k = 0
Rule:
The default parameters must be the in the last position.
function( int p1 = 0, int p2 = 10, int p3 ); // compilation error.
function( int p3, int p1 = 0, int p2 = 10 ); // OK
| C |
C++ |
function();
the function can have any parameters, default return value is integer.
|
void function( int param1, float param2 );
The parameters of a function are specified and checked when the function is
implemented. |
Function overloading
In C++, several functions can have the same name. The compiler knows which
function to choose by looking at the number and type of function parameters.
Example :
// interface
int square( int number );
float square( float number );
double square( double number );
Operator overloading
| C |
C++ |
int a = b + c; |
int a = b.operator+(c) |
Traditional operators (=, ==, <, >, !=, +, +=, -, -=, *, *=, etc.)
can be defined on non-standard data types (vectors, matrices, complex numbers,
etc.)
Execution is slowed down by function calls (one function call amounts to stacking the function, copying of its parameters, executing, and retrieving the function parameters and return value from the stack).
C++ offers inline functions, which must be defined in header files. It is a safe alternative to C's #defines directives.
| C |
C++ |
#define MAX(x, y) ( x > y ? x : y )
C macros are not compiled, and replaced directly in the code by the
preprocessor.
Problems:
There is no type checking of the macro parameters
What happens there ?
int maxi = MAX( a+=3, b+=4 );
|
inline int MAX( int x, int y ) {
return x > y ? x : y;
}
This code is compiled, type checking is made.
The function code is copied inline in the client code. |
Limits:
- Debugging an inline function is impossible (note that some compilers do not inline functions in debug mode)
- Compilation time is increased: modifying the implementation of an
inline function requires recompiling all files that use this function.
- As a consequence, the definition of inline functions should be delayed
to the optimization phase, when the code is written, running OK, and is
just too slow.
C++ has its boolean type (bool) :-))
It should be used without restriction, along with the keywords true
and false.
It is possible (but no recomended) to convert a bool to another built-in type:
int int_value = 0;
bool bool_value = int_value; // bool_value is false
bool_value = int_value + 10; // bool_value becomes true
white characters (wchar_t) (16 bits),
allow to code arabic, cyrillic characters.
After compilation, the "object code" does not keep track of the initial
language. Hence, functions written in C, pascal, FORTRAN can be linked
with C++ code. What your C++ compiler will need is just a definition of
the functions' interface:
// interface
extern "C" int c_function( int param1, float param2 );
The keyword const specifies if a variable has a read-only status
or a read-write status.
Examples:
| A variable pointer to variable characters |
A constant pointer to variable characters |
A variable pointer to constant characters |
A constant pointer to constant characters |
char* c1; |
char* const c2; |
const char* c3; |
const char* const c4; |
This keyword in particularly interesting in function definitions :
a function that does not edit its parameters should always use the const
keyword. Defining explicitely which variables can be modified by a
function in important for two reasons:
- he action of the function is more explicit to the developper who wants to
use it.
- The execution is optimized.
Conversion between const and non const pointers is a classical source of
compilation errors. The idea is that something variable can be cast
into something const, while the reverse is not true.
ExerciseGiven that, try to fill in the table below:
| line 2 \ Line 1 |
char* p2 |
const char* p2 |
char* const p2 |
const char* const p2 |
char* p1 = p2; |
|
|
|
|
const char* p1 = p2; |
|
|
|
|
char* const p1 = p2;
char* const p1; p1 = p2; |
|
|
|
|
const char* const p1 = p2;
char* const p1; p1 = p2; |
|
|
|
|
Solution
C++ provides a mechanism to maniputate references to objects as if the reference was the object itself. This has the same goal as pointers, but the syntax and use of references is slightly different:
| pointer |
reference |
int array[10];
int* pointer = &array[5];
int value = *pointer;
*pointer = 100; // array[5] == 100
|
int array[10];
int& ref = array[4];
int value = ref;
ref = 100; // array[5] == 100
|
| Can be allocated or not |
Always stands for a valid, allocated, variable |
Rules:
- A reference must be initialized;
- A pointer must be used if the variable can remain uninitialized
- In function parameters, an optimization consists in replacing
variables of "large type" by const references to avoid a
time-consuming copy of the variable.
- In a function, never return a reference to a local variable.
This is accepted by some compilers, but generates tricky errors
(dangling reference).
| C |
C++ |
| Variables are declared at the beginning of procedures. |
Variables can be declared where necessary. |
rules
- Declare one variable per line
- Declaration and initialisation should be done at the same time.
- Declare a variable as late as possible in the code
(more efficient, more safe, and more readable).
Example
float compute_array_mean( float* array, int dim ) {
if( dim == 0 ) {
printf("Cannot compute mean of a void array\n");
abort();
}
float sum = 0.;
{for( int i=0; i<dim; i++ ) {
sum += array[i];
}}
return sum / dim;
}
Allocation
| C |
C++ |
T* var = (T*)malloc( sizeof(T) );
T* array = (T*)malloc( n * sizeof(T) );
Memory allocations are not typed. |
T* var = new T;
T* array = new T[n];
|
There is no pre-defined C++ equivalent to realloc.
| C |
C++ |
void* tab = malloc( n * sizeof( T ) );
void* larger = realloc(
tab, 2 * n * sizeof( T )
);
|
T* tab = new T[n];
|
Deallocation
| C |
C++ |
free( var );
free( array );
|
delete var; // var == nil
delete [] array; // array == nil
|
Rules
- a pointer allocated with
new[] must be deallocated with
delete[]
- Avoid the use of
malloc, calloc, realloc, free in any C++
code.
|
C |
C++ |
| Library |
stdio.h |
iostream,
fstream,
stringstream |
| Functions |
printf --> standard output
scanf <-- standard input
fprintf --> FILE*
fscanf <-- FILE*
sprintf --> char[N}
sscanf <-- char*[N]
|
cout --> standard output
cerr --> standard error output
cin <-- standard input
ofstream --> output file
ifstream <-- input file
ostrstream --> char*[N]
istrstream <-- char*[N]
ostream& operator<< insertion operator
istream& operator>> extraction operator
|
| Example |
int i = 10;
float factor = 0;
printf( "variable: %d\n", i);
printf( "factor: ");
scanf( "%f", &factor);
...
|
int i = 10;
float factor = 0;
cout << "variable: " << i << endl;
cout << "factor: ";
cin >> factor;
...
|
| Remarks |
I/O formats are described separately from parameters |
Extraction / insertion operators can be overloaded for any type.
Execution is faster.
All i/o objects are defined in the standard namespace, hence must be prefixed by std::, unless the instruction using namespace std; has beenwritten above in the code. |
Overloading insertion/extraction operators
Example:
struct Complex {
float x_;
float y_;
};
ostream& operator<<( ostream& os, const Complex& z) {
return os << z.x_ << " + " << z.y_ << * j";
}
int main( int argc, char * argv[] ) {
Complex z;
z.x_ = 1.5;
z.y_ = 0.5;
cout << "Value = " << z << endl;
return 0;
}
The output of this small program is :
Value = 1.5 + 0.5 * j
Iostreams Manipulators
Exercise
Write a small program to represent an histogram with ascii characters.
For the sake of simplicity, the histogram can be generated as an array of
random values generated with the function rand()
(in stdlib.h).
Solution
|