Introduction à C++ 2011 (C++0x)

Formation

En Ligne

Prix sur demande

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Description

  • Typologie

    Formation

  • Méthodologie

    En ligne

Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Questions / Réponses

Ajoutez votre question

Nos conseillers et autres utilisateurs pourront vous répondre

À qui souhaitez-vous addresser votre question?

Saisissez vos coordonnées pour recevoir une réponse

Nous ne publierons que votre nom et votre question

Les Avis

Les matières

  • C++

Le programme

Introduction du cours

Bonjour à tous !
Vous avez entendu parler de la nouvelle norme C++, ces derniers temps ?
C++0x et C++1x vous disent-ils quelque chose ?
Vous voulez en apprendre plus sur cette norme ?
Alors, vous êtes au bon endroit !

Ce tutoriel a pour but de vous montrer quelques nouveautés de la norme C++ 2011 qui sont déjà utilisables par les compilateurs actuels.

Pourquoi une nouvelle révision du langage C++ ?

La norme actuelle du C++ date de 1998, puis a été mise à jour en 2003.
Nous pouvons nous demander pourquoi faire une nouvelle norme maintenant.

Pour répondre à la question, je cite Wikipédia :

Citation : Wikipédia

Un langage de programmation comme le C++ suit une évolution qui permettra aux programmeurs de coder plus rapidement, de façon plus élégante et permettant de faire du code maintenable.

Certaines nouveautés permettent en effet d’écrire du code C++ plus simple, comme vous le verrez dans quelques instants.

En outre, le matériel informatique évoluant constamment, en particulier les processeurs, les besoins sont maintenant différents en matière de programmation.
Ayant aujourd’hui plusieurs cœurs, les processeurs peuvent exécuter plusieurs instructions en même temps.
Des classes permettant l’utilisation de la programmation concurrente sont ainsi apparues dans cette norme du C++.

Est-ce que la nouvelle norme du C++ est rétro-compatible avec l’ancienne ?

Presque.
La plupart du code déjà écrit n’aura donc pas besoin d’être modifié.

Préparation du compilateur pour utiliser C++ 2011

Attention : tout ceci est expérimental et ne devrait pas être utilisé en production.
Utilisez ces nouveautés à vos risques et périls.

De plus, les compilateurs actuels n’implémentent pas toutes les nouveautés de la norme C++0x.
Les codes de ce tutoriel ont été testés avec g++ 4.6 sous GNU/Linux.

Je vais vous montrer comment configurer g++ 4.6 sur Ubuntu pour utiliser les nouveaux standards du C++.

Installation

Premièrement, ajoutez ce ppa à vos sources :

deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu YOUR_UBUNTU_VERSION_HERE main

en modifiant YOUR_UBUNTU_VERSION HERE par votre version d’Ubuntu (natty, maverick, lucid ou karmic).

Vous pouvez maintenant mettre à jour le compilateur g++ :

sudo apt-get install g++

Après l’installation, tapez la commande suivante pour vérifier que vous avez au moins la version 4.6 de g++ :

g++ -v

La dernière ligne de la sortie devrait vous le confirmer :

gcc version 4.6.1 20110409 (prerelease) (Ubuntu 4.6.0-3~ppa1)Configuration de la compilation

Maintenant, vous allez essayer de compiler ce programme :

#include <iostream> int main() { int tableau[5] = {1, 2, 3, 4, 5}; for(int &x : tableau) { std::cout << x << std::endl; } return 0; }

en utilisant la ligne de commande habituelle :

g++ main.cpp -o BoucleFor

J’ai la bonne version de g++, mais j’obtiens tout de même une erreur du compilateur.
Que dois-je faire ?

Vous devez simplement lui demander poliment de compiler en utilisant la nouvelle norme.
Sinon, vous pouvez avoir une erreur de ce type :

main.cpp: In function ‘int main()’: main.cpp:5:18: error: range-based-for loops are not allowed in C++98 mode

Parfois, vous pouvez avoir un avertissement du genre :

main.cpp:3:1: warning: scoped enums only available with -std=c++0x or -std=gnu++0x [enabled by default]

qui vous indique un peu mieux que faire.

Lancez donc la dernière commande avec un nouvel argument :

g++ -std=c++0x main.cpp -o BoucleFor

Et voilà, ça compile !

Le précédent code source vous met-il l’eau à la bouche ?
Si c’est la cas, alors continuez votre lecture, vous n’avez pas terminé de découvrir de nouvelles façons d’écrire du code plus simplement qu’avant !

Les énumérations fortement typées

Pour commencer, voyons quelque chose de facile : les énumérations fortement typées.

Les énumérations

Vous connaissez déjà les énumérations, que l’on peut créer comme ceci :

enum Direction { HAUT, DROITE, BAS, GAUCHE };

On peut ensuite créer une variable utilisant cette énumération :

Direction direction = HAUT;

Puis, nous pouvons afficher cette variable :

std::cout << direction << std::endl;

Cela fonctionne, car il y a une conversion implicite vers un entier.
C’est comme si nous avions fait ceci :

std::cout << static_cast<int>(direction) << std::endl; Les énumérations fortement typées

Passons aux nouveautés.
Les énumérations fortement typées se créent en ajoutant le mot-clé class après enum. :

enum class Direction { Haut, Droite, Bas, Gauche };

Par la suite, pour l’utiliser, il faut utiliser le nom de l’énumération, suivi par l’opérateur de résolution de portée et du nom de la valeur que l’on souhaite utilisée :

Direction direction = Direction::Haut;

C’est la première différence avec les énumérations que vous connaissez.

La seconde, c’est qu’il n’y a pas de conversion implicite vers un entier.
Ainsi, le code suivant ne compilera pas :

std::cout << direction << std::endl;

Pour qu’il compile, il faut explicitement convertir la variable en entier :

std::cout << static_cast<int>(direction) << std::endl;

Mais, à quoi ça sert d’utiliser ces énumérations ?
C’était bien plus simple avant, non ?

Ces énumérations n’ont pas été créées pour le simple plaisir de compliquer les choses.
Elles permettent d’assurer une certaine sécurité en codant.

En effet, vous ne voulez sûrement pas faire des opérations mathématiques avec votre énumération.
Le code suivant n’aurait aucun sens :

int nombre = 5 + Direction::Droite;

Ce code provoque une erreur de compilation et c’est bien, car ça n’a pas de sens d’ajouter une direction à un nombre.

Le « bug » des chevrons

Vous souvenez-vous de la façon de créer un vecteur de vecteurs ?
Il y a un petit point à ne pas oublier ; il faut mettre un espace entre les deux derniers chevrons :

std::vector<std::vector<int> > nombres;

Sinon, >> pourrait être confondu avec l’opérateur de flux.

Eh bien, savez-vous quoi ?
Il est maintenant possible d’écrire ceci en C++0x :

std::vector<std::vector<int>> nombres;

Il n’y a plus d’erreur de compilation.

Les listes d’initialisateursCôté utilisateur

Pour créer un vecteur avec différentes valeurs initiales, il faut écrire plusieurs lignes de code :

std::vector<int> nombres; nombres.push_back(1); nombres.push_back(2); nombres.push_back(3); nombres.push_back(4); nombres.push_back(5);

Il est maintenant possible de faire beaucoup plus court avec une liste d’initialisateurs :

std::vector<int> nombres = { 1, 2, 3, 4, 5 };

C’est comme si on initialisait un tableau normal :

int nombres2[] = { 1, 2, 3, 4, 5 };

sauf qu’on peut le faire avec les conteneurs de la STL.

Voici un code complet présentant comment utiliser une liste d’initialisateurs avec un std::map :

#include <iostream> #include <map> #include <ostream> #include <string> int main() { std::map<int, std::string> nombres = { { 1, "un" }, { 2, "deux" }, { 3, "trois" } }; std::cout << nombres[1] << std::endl; }

Simple, non ?

Côté créateur

Vous avez utilisé les listes d’initialisateurs, mais aimeriez-vous créer des fonctions qui les utilisent ?
C’est ce que nous allons voir ici.

Nous allons créer une fonction très banale qui ne fait qu’afficher les éléments passés à la fonction à l’aide de la liste d’initialisateurs.

Notre fonction main() sera :

int main() { afficherListe({ 1, 2, 3, 4, 5 }); return 0; }

Nous souhaitons implémenter la fonction afficherListe() qui affichera les éléments envoyés.

La fonction aura l’allure suivante :

void afficherListe(std::initializer_list<int> liste) { //Affichage de la liste. }

Étant donné que nous avons indiqué int entre les chevrons, nous acceptons seulement les entiers dans la liste.

Le code de la fonction est donc :

void afficherListe(std::initializer_list<int> liste) { for(std::initializer_list<int>::iterator i(liste.begin()) ; i != liste.end() ; ++i) { std::cout << *i << std::endl; } }

N’oubliez pas d’inclure l’entête :

#include <initializer_list> Le mot-clé auto

Le mot-clé auto est un mot-clé utilisable en C++0x qui est différent du mot-clé auto qui était utilisé avant. Il est possible de le mettre à la place du type de telle sorte que ce type est automagiquement déterminé à la compilation en fonction du type retourné par l’objet utilisé pour l’initialisation.
On parle ici d’inférence de types.

Donc, il est possible d’écrire le code suivant :

auto nombre = 5;

La variable nombre sera de type entier (int).
Mais, je vous interdis de faire cela !
Si vous initialisez toutes vos variables avec le mot-clé auto, votre code va vite devenir illisible.

Vous devez utiliser ce mot-clé le moins possible.

As-tu des exemples d’utilisation ?

Oui, en voilà un :

for(auto i(nombres.begin()) ; i != nombres.end() ; ++i) { std::cout << *i << std::endl; }

Au lieu de cela :

for(std::vector<int>::iterator i(nombres.begin()) ; i != nombres.end() ; ++i) { std::cout << *i << std::endl; }

Mais, encore là, c’est limite.

Si vous utilisez beaucoup de fois un même itérateur, le mieux est de créer un typedef :

typedef std::vector<int>::iterator iter_entier; for(iter_entier i(nombres.begin()) ; i != nombres.end() ; ++i) { std::cout << *i << std::endl; }

Vous pouvez également utiliser ce mot-clé lorsqu’une même fonction peut retourner différents types de données.

Vous devez faire attention à deux points en particulier :

  1. Le mot-clé auto n’est pas comme un type qui change au cours de l’exécution du programme. Si j’assigne un entier à une variable dont le mot-clé auto est utilisé à l’initialisation, cette variable sera et restera un entier.

  2. Vous devez obligatoirement initialiser une variable déclarée avec le mot-clé auto à la déclaration. Autrement dit, il est impossible de faire :

    auto variable;

Si je déclare une variable avec le mot-clé auto, puis-je donner le même type à une autre variable ?

Oui, avec le mot-clé decltype :
Ce mot-clé détermine le type d’une expression.

auto variable = 5; decltype(variable) autreVariable;

Ainsi, nous sommes sûr que autreVariable aura le même type (int) que variable.

L’inférence de type est très utile lorsque nous utilisons la programmation générique.
Considérez ce programme :

#include <iostream> template <typename T> T maximum(const T& a, const T& b) { if(a > b) { return a; } else { return b; } } template <typename T> T minimum(const T& a, const T& b) { if(a < b) { return a; } else { return b; } } int main() { int a(10), b(20); auto plusGrand = maximum(a, b); decltype(plusGrand) plusPetit = minimum(a, b); std::cout << "Le plus grand est : " << plusGrand << std::endl; std::cout << "Le plus petit est : " << plusPetit << std::endl; return 0; }

Ce code détermine, grâce à des fonctions génériques, le plus grand et le plus petit nombres.

Ce qui est intéressant, c’est que nous n’avons besoin que de modifier la première ligne de la fonction main() si nous voulons essayer ce code avec un autre type.
Remplacez-la par celle-ci et tout fonctionne :

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Introduction à C++ 2011 (C++0x)

Prix sur demande