© 2015 Ghyslain Leclerc, CHUQ-UL
La programmation représente la rédaction du code source d'un logiciel.
Instructions lisibles par un humain
Instructions lisibles par un processeur
Facteurs à considérer → choix d'un langage :
↑
↓
C++
↓
↑
↑
~
↑
Souvent en conflit
C++
Selon Wikipedia :
Un paradigme de programmation est un style fondamental de programmation informatique qui traite de la manière dont les solutions aux problèmes doivent être formulées dans un langage de programmation
Clinique
À rechercher en écrivant du code :
Autant que possible...
var_type var_name;
int age;
double sales_tax;
char my_char;
int a
average = ( 5 + 7 ) / 2
multi_strings = tokenize( single_string )
Combinaisons de:
qui sont interprétés selon les règles de précédences d'un langage et qui redonnent une valeur (qu'elle soit utilisée ou pas).
int a (aucune sous-expression)
average = ( 5 + 7 ) / 2
( 5 + 7 -> "sous-expression" 1 )
( résultat précédent / 2 -> "sous-expression" 2 )
( etc. )
Une expression peut être composée d'expressions.
Expression Instruction
---------- -----------
int a int a;
( 5 + 7 ) / 2 ( 5 + 7 ) / 2;
Exemple de C++
begin program
statement 1
statement 2
statement 3
...
statement n
end program
begin function1( no_entrants )
statement f1_1
statement f1_2
return value_f1
end function1
begin function2( 1_entrant )
value_f2 = 1_entrant + 5
return value_f2
end function2
begin program
var1 = call function1()
call function2( var1 )
end program
Concept de scope expliqué plus loin
begin function1 begin procedure1
statement f1_1 statement m1_1
statement f1_2 // no return statement
return value_f1 end procedure1
end function1
Selon Wikipedia :
En informatique, la portée (scope en anglais) d'un identifiant est l'étendue au sein de laquelle cet identifiant est lié.
In other parts of the program the name may refer to a different entity (it may have a different binding), or to nothing at all (it may be unbound).
int varGlobale;
namespace ns
{
int varLocale_Namespace;
}
void fn( int input )
{
int varLocale_Fonction;
if( true )
{
int varLocale_If;
}
// varLocale_If non disponible ici
// varLocale_Namespace non disponible sauf si qualifiée (ns::varLocale_Namespace)
}
// varLocale_Fonction non disponible ici...
[Pseudo code, aucun langage]
1 print "Combien de bonjour vous voulez (1 ou 2) ?"
2
3 lire valeurLue
4
5 if valeurLue == 1:
6 goto 9
7
8 print "Bonjour"
9 print "Bonjour"
10
[Pseudo code, aucun langage]
1 print "Combien de bonjour vous voulez (1 ou 2) ?"
2
3 lire valeurLue
4
5 int compteur = 0
6 for( compteur != valeurLue, increment compteur )
7 {
8 print "Bonjour"
9 }
10
===========
do
...
while bool_exp
===========
while bool_exp
do
...
===========
if bool_exp
...
else if bool_exp
...
else
...
===========
for( pre-processing ; bool_exp ; post-processing )
...
int a
int b
...
if ( a + 3 > b - 6 ) print "vrai"
Expression seulement, pas instruction.
Forme de programmation où l'on décrit les opérations en séquences d'instructions exécutées par l'ordinateur pour modifier l'état du programme.
std::string myStrgVar;
std::string mySTRGVar; // OK, not the same
int a = 8;
int a = 8; // exactly the same
void myFn( int firstIn
double secondIn );
if
{
// statements
}
int const a;
type
modificateur(s)
nom
int* a;
int *a;
Variable de type pointeur:
Variable de type référence:
int& b = a;
int &b = a;
// illegal
int main( int argc, char* argv[] )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int fn1()
{
SecondType var1;
SecondType* var2;
var2 = new SecondType;
}
int fn2()
{
ThirdType var1;
int var2;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int fn1()
{
SecondType var1;
SecondType* var2;
var2 =
new SecondType;
}
int fn1()
{
SecondType var1;
SecondType* var2;
var2 =
new SecondType;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int fn1()
{
SecondType var1;
SecondType* var2;
var2 =
new SecondType;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int fn2()
{
ThirdType var1;
int var2;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int fn2()
{
ThirdType var1;
int var2;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int main( ... )
{
int var1;
FirstType* var2;
var2 = new FirstType;
fn1()
fn2()
delete var2;
return SUCCESS;
}
int a( 4 ); // a is on the stack
int& b( a ); // b is on the stack because it's a
int* c( new int ); // c is on the stack.
*c = 4; // what c points to is on the heap
// so 0xf6a340bc is on the stack
// and 4 is on the heap
int* d( &a ) // d is on the stack and points to
// the stack
int a( 4 ); // equivalent to int a = 4;
int& b = a;
int* c = new int;
*c = 4;
printf( "%i", a ); // outputs 4 to the screen
printf( "%i", b ); // outputs 4 to the screen
printf( "%i", c ); // outputs address of c to the screen
printf( "%i", *c ); // outputs 4 to the screen
Variable et référence:
Pointeur
Compilation
Communication
Dis au compilateur de ne pas permettre de modification de la variable.
Promet à l'utilisateur du code que la variable ne changera pas après l'appel de fonction.
Erreurs de compilation
Lire de droite à gauche
//*** Read from right to left ***
int const* a; // pointer to const int
// pointer can change, pointee can't
int* const a; // const pointer to int
// pointer can't change, pointee can
int const* const a; // const pointer to const int
// pointer and pointee can't change
double convertToDbl( std::string const& strg );
sortie
entrant(s)
nom
#include <string>
double convertToDbl( std::string const& strg )
{
int a; // accessible only in the function body
strg = "nouvelle valeur"; // forbidden because of the const
double convertedVal = atod( strg.c_str() );
return convertedVal;
}
Dans le scope de la fonction, seules les variables globales, définies localement et d'entrées sont disponibles.
double convertToDbl( std::string const& strg );
Les entrants d'une fonction peuvent l`être de quelques façons différentes :
En réalité, tout est passé par copie...
double convertToDbl( std::string strg );
double convertToDbl( std::string& strg );
double convertToDbl( std::string* strg );
Si fonction...
void fn1( std::string& willBeModified )
{}
std::vector< double >
fn2( std::string const& willNotBeModified )
{}
struct Staff
{
int id
char* lastName;
char* firstName;
char* jobTitle;
};
void fn_A( int staffID, char* lastName, char* firstName, char* jobTitle );
void fn_B( Staff& staff );
#include <string> // (1)
#include <iostream> // (1)
int a; // (2)
int* b; // (3)
int& c; // (4)
double fn( std::string const& nbStrg ); // (5)
double fn( std::string const& nbStrg )
{
double converted;
... // (6)
return converted;
}
Forme de programmation où l'on génère des objets (données + routines) pour représenter les concept du programme.
L'interaction entre ces objets, via leurs relations, permet de concevoir et réaliser les fonctionnalités attendues et de résoudre le ou les problèmes.
struct Staff
{
int id
char* lastName;
char* firstName;
char* jobTitle;
};
struct StaffVector
{
Staff* allStaff[];
int nbInVec;
};
void addStaff( StaffVector& vec, Staff const& staffToAdd )
{
// Add staffToAdd
++(vec.nbInVec);
}
void removeStaff( StaffVector& staffVec, int idx )
{
// Remove staff at index idx
--(vec.nbInVec);
}
void addStaff( StaffVector& vec, Staff const& staffToAdd )
{
// Add staffToAdd
++(vec.nbInVec);
}
void removeStaff_Better( StaffVector& vec, int idx )
{
// Remove staff at index idx
// Oups, forgot to decrement
}
Cohérence de la structure StaffVector est perdue
struct Staff
{
...
};
class StaffVector
{
public:
add( Staff const& staff );
remove( int idx );
private:
Staff* allStaff_[];
int nbInVec_;
};
class StaffVector
{
public:
add( Staff const& staff );
remove( int idx );
int myMemberPublicData;
private:
Staff* allStaff_[];
int nbInVec_;
};
void fn1( Staff& staff )
{
staff.nbInVec_ = 5; // compile no, priv. and non-member fn
staff.myMemberPublicData = 8; // compile ok, bad practice
}
class StaffVector
{
public:
add( Staff const& staff );
remove( int idx );
private:
Staff* allStaff_[];
int nbInVec_;
};
struct StaffVector
{
Staff* allStaff[];
int nbInVec;
};
class StaffVector
{
public:
add( Staff const& staff );
remove( int idx );
private:
Staff* allStaff_[];
int nbInVec_;
};
struct StaffVector
{
public:
add( Staff const& staff );
remove( int idx );
private:
Staff* allStaff_[];
int nbInVec_;
};
Équivalent, sauf pour l'accès par défaut, c.-à-d. sans spécification.
struct StaffVector
{
add( Staff const& staff );
remove( int idx );
Staff* allStaff_[];
int nbInVec_;
};
class StaffVector
{
public:
add( Staff const& staff );
remove( int idx );
Staff* allStaff_[];
int nbInVec_;
};
struct StaffVector
{
private:
add( Staff const& staff );
remove( int idx );
Staff* allStaff_[];
int nbInVec_;
};
class StaffVector
{
add( Staff const& staff );
remove( int idx );
Staff* allStaff_[];
int nbInVec_;
};
=
=
Recette pour créer un type d'objet.
Résultat de l'application de la recette pour créer un objet spécifique.
#include <string>
class Staff
{
int id_;
std::string lastName_;
std::string firstName_;
std::string jobTitle_;
};
#include "Staff.h"
// Instances
// Object 1
Staff staff1( 0, "Leclerc",
"Ghyslain", "Phys" );
// Object 2
Staff staff2( 1, "Brouard",
"Lucie", "Tech" );
Staff.h
Privée par défaut
class StaffVector
{
public:
void remove( int idx );
int size() const;
private:
Staff* allStaff_[];
int nbInVec_;
};
Indique si une fonction membre peut ou pas être utilisée sur un objet const.
StaffVector staffVec1;
StaffVector const staffVec2;
staffVec1.size() // Compile ok, non-const object, const fn
staffVec2.size() // Compile ok, const object, const fn
staffVec1.remove() // Compile ok, non-const object, non-const fn
staffVec2.remove() // Compile no, const object, non-const fn
#include <string>
class Staff
{
public:
Staff(); // Default ctor : called when no args
Staff( std::string lastName,
std::string firstName ); // Nondescript ctor
Staff( Staff const& staff ); // Copy ctor : creates a duplicate
private:
int id_;
std::string lastName_;
std::string firstName_;
std::string jobTitle_;
};
#include <string>
class Staff
{
public:
Staff();
Staff( Staff const& staff );
~Staff(); // dtor
private:
int id_;
std::string lastName_;
std::string firstName_;
std::string jobTitle_;
};
#include <string>
class Staff
{
public:
int id() { return id_; } // Getter, access value of id_
void setId( int newId ) { id_ = newId; } // Setter, change value of id_
private:
int id_;
std::string lastName_;
std::string firstName_;
std::string jobTitle_;
};
#include <string>
class Staff
{
public:
Staff& operator=( Staff const& staff ); // Assignment operator
void operator++() { jobTitle_ = "Président"; } // increment operator
private:
int id_;
std::string lastName_;
std::string firstName_;
std::string jobTitle_;
};
#include <string>
class Staff
{
public:
// better than increment operator
void promote() { jobTitle_ = "Président"; }
private:
int id_;
std::string lastName_;
std::string firstName_;
std::string jobTitle_;
void myPrivateFunc_();
};
#include <string>
class Staff
{
...
};
void fn( Staff& staff );
// Let a class Staff exist and be defined in Staff.h
#include "Staff.h"
Staff staff1; // call ctor by default
Staff staff2(); // call ctor by default
Staff* staff3 = new Staff(); // call ctor by default
staff1.fn1(); // call fn1 on object staff1
staff3->fn1(); // call fn1 on object staff3
fn2( staff2 ); // call non-member function fn2 on staff2
-(*staff3); // call unary operator - on staff3
staff1 + staff2; // call binary operator +
Le compilateur génère quelques fonctions par défaut si non écrites [C++03] :
class A
{
A();
A( A const& a );
A& operator=( A const& a );
~A();
};
class A
{
A() = default;
A( A const& a ) = default;
A& operator=( A const& a ) = default;
~A() = default;
};
Celui auquel le mot fait généralement référence
int fn1( int input1 );
int fn1( double input1 );
int fn1( std::string input1 );
// illegal, no overloading on return value
double fn1( int input1 );
class ObjectThanCanFly
{
virtual void fly();
virtual double currentAltitude() const;
};
class Bird : public ObjectThanCanFly
{
// gets functions fly() and currentAltitude() whitout defining them
};
class Plane : public ObjectThanCanFly
{
// gets functions fly() and currentAltitude() whitout defining them
};
void make_fly( ObjectThatCanFly& obj )
{
obj.fly();
}
int main( int argc, char* argv[] )
{
Bird theBird;
Plane thePlane;
make_fly( theBird );
make_fly( thePlane );
}
Significatif ? Si appellé 1 000 000 de fois, peut-être. Vérifier au besoin.
template< typename T >
void swap( T& left, T& right )
{
T temp( left );
left = right;
right = temp;
}
int a( 6 ), b( 7 );
double c( 4.5 ), d( -5.7 );
swap( a, b ); // create with T == int
swap( c, d ); // create with T == double
void swap( int& left, int& right )
{
int temp( left );
left = right;
right = temp;
}
void swap( double& left, double& right )
{
double temp( left );
left = right;
right = temp;
}
Définition d'algorithmes identiques opérant sur des données de types différents spécifiés plus ultérieurement.
#include "lib1/string.h"
#include "lib2/string.h"
void myCoolFunction()
{
string nameOfUser( "" ); // which string -> name clash
}
#include "lib1/string.h"
#include "lib2/string.h"
void myCoolFunction()
{
lib1::string nameOfUser( "" ); // using string class of lib1
}
main.cxx
namespace lib1
{
class string{};
} // namespace lib1
namespace lib2
{
class string{};
} // namespace lib2
lib1/string.h
lib2/string.h
namespace blabala
{
void fn1();
namespace inner // can be nested
{
void fn2();
} // namespace inner
} // namespace blabala
namespace blabala // are additive (as opposed to classes and structs)
{
void fn3();
} // namespace blabala
// Different calls for fn1.
::fn1(); // At global scope. If not found, error.
::std::fn1(); // From the namespace std at the global scope. If not found, error.
std::fn1(); // From the namespace std at current scope.
// If not found, try to find namespace std in global scope
// and fn1 in that one.
// If not found, error.
computer > g++ -o nom_executable main.cpp
Fichiers que l'on écrit
Compilateur
Options du compilateur
computer > cl.exe /EHsc main.cxx
Deux étapes :
C'est ici que l'on code
computer > g++ -c file1.cxx
computer > g++ -c file2.cxx
...
computer > g++ -c main.cxx
computer > g++ -o nom_executable file1.o file2.o ... main.o
Raccourci pratique pour appeler le compilateur et l'éditeur de lien
// declaration in header file
void fn();
// definition in source file
void fn()
{
}
file.h
file.cpp
#ifndef PATH_TO_FILE_H_
#include <string>
#include <external_lib_header>
#include "my_other_h_file.h"
void fn();
#endif // PATH_TO_FILE_H_
file.h
#ifndef PATH_TO_FILE_H_
#include <string>
#include <iostream> // Useless here. Put in .cxx file
std::string fn( int a );
#endif // PATH_TO_FILE_H_
file.h
#ifndef PATH_TO_FILE_H_
#include <string>
using namespace std; // Never in a header file.
// Pollutes every file which includes this one.
void fn();
#endif // PATH_TO_FILE_H_
file.h
#ifndef PATH_TO_FILE_H_
#include <string>
using std::string; // Pollutes every file which includes this one.
void fn();
#endif // PATH_TO_FILE_H_
file.h
#ifndef PATH_TO_FILE_H_
void fn();
#include "file.inl"
#endif // PATH_TO_FILE_H_
//!
//! @brief This functions does that
//!
inline void fn()
{
// do something
}
file.h
file.inl
#ifndef PATH_TO_FILE_H_
void fn();
#endif // PATH_TO_FILE_H_
//!
//! @brief This functions does that
//!
void fn()
{
// do something
}
file.h
file.cpp
#ifndef PATH_TO_FILE_H_
#include "projet/code.h" // pcq dossier 'projet'
// dans dossier 'include'
#endif // PATH_TO_FILE_H_
#ifndef PATH_TO_CODE_H_
#include "code.inl" // pcq .inl avec .h
#endif // PATH_TO_CODE_H_
file.h
code.h
Projet en solo → ce que vous voulez
Dès que N > 1 → s'entendre sur un style
Compromis
Facilité de lire le code des collègues
P.ex. : Vous et votre directeur...
Nomenclature
Espaces blancs (espaces/tabs, lignes, etc)
Position des accolades
Nom des fichiers
Organisation des fichiers
Utilisation d'un namespace
Initialisation des variables
Casse vs tiret bas pour longs noms
void thisLongFunctionName();
void this_long_function_name();
Utilisation de la casse pour le type d'identifiant
#define MACROS_ALL_UPPER_CASE
namespace all_lower_case
{
class ClassesStartWithCapital()
{};
void functionsStartWithLowerCase();
int varsAreLikeFunctions;
}
Statuer sur les abréviation
(+) Réduit la longueur de ligne
(-) Augmente l'ambiguité
Trop peu : code difficile à lire
Trop : code difficile à lire
Laisser trop de lignes entre les fonctions n'est pas mieux que pas assez.
Tabs vs spaces:
Les gens s'entendent sur un point :
Lequel des deux, combien d'espace(s):
Guerre religieuse et dangereuse
Tabs OU espaces
PAS les deux.
Ouverture sur la ligne de début ou la suiante.
void myFn()
{
if( myBool )
{
// statement
}
else
{
// statement
}
}
void myFn()
{
if( myBool ) {
// statement
} else {
// statement
}
}
void myFn()
{
if( myBool ) {
// statement
}
else
{
// statement
}
}
void myFn()
{
if( myBool )
{
// statement
}
else
{
// statement
}
}
Whitesmiths
Allman
K&R
Stroustrup
Celui que je préfère
Les warnings émis par le compilateur devraient être minimisés.
Pas des erreurs de langage, mais :
mauvaises pratiques;
erreurs de logique.