C++ as a better C

Guillaume Caumon, Fev 2000, revised Jan 2001 and Jan 2005. .


[Prev: Introduction] [Next: Introduction to classes]
 


Interface and implementation

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


Comments

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


Prototypes and default arguments

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


Type Checking

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.


Overloading

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


Inline functions

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.


    Pre-defined types

    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.


    External linkage

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


    Const keyword

    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 


    References

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


    Simple variables declaration

    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;
    }
    
    
     


    Memory allocation / deallocation : operator new, operator delete

    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.
     


    I/O

    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

    • << endl ends a line, inserts a "\n" char and flushes the stream.

    • dec, hex, oct allow to change the format of streams.

    • >> ws extracts white characters

    • << ends ends a string, inserting a \0

    • << flush empties the outstream

    • >>setw(n) sets the number of chars of the stream:
       
          int d;
          cin >> setw(3) >> d; // reads an integer d with 3 characters
        

    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 


    [Introduction] [Next: Introduction to classes]