Article in Programmez for January 2019

My next article will be in french Magazine Programmez in January 2019. It will talks about rootkits and key logger.

225

Advertisements

Visual Studio 2019 Live Share

With LiveShare feature, you can do remote debugging and remote coding from and to VS &nd VS Code:

Microsoft Connect(); 2018 KeyNote !

https://blogs.msdn.microsoft.com/dotnet/2018/12/04/announcing-net-core-3-preview-1-and-open-sourcing-windows-desktop-frameworks/

En vrac:

  • Visual Studio Code Collaboration Mode
  • Visual Studio 2019 Preview
  • Intellicode : Intellisence avec machine learning
  • Nouveautés Github
  • NET Core 3
  • Open Source WinForms, WPF et XAML UI
  • Nouveautés Azure Cognitive Services

Microsoft Connect() ; 2018

You as developers more productive.

The world runs on software.

It’s a great time to be a developer

Next generation of softwares

Powerful open in a productive way

Connected Apps

Intellicode vs code extension

Azure App Service

Python function

VS Code and VS collaboration

Microsoft loves open source

Nat GitHub CEO

CD with Github

Build your pipeline

VS2019

.NET Core 3

New capabilities

Fix my code

Power of Intellicode

Azure

AI

Articles C++ en Serie…

OOP : http://bit.ly/2rhQFYl

Basic – Partie 1 : http://bit.ly/2FT92Nn

Les classes – Partie 2 : http://bit.ly/2FROSDK

Les Templates – Partie 3 : http://bit.ly/2zwZ4fd

STL – Partie 4 : http://bit.ly/2Sp6kAJ

Boost – Partie 5: http://bit.ly/2U7r4OX

 

La Programmation Orientée Objet (OOP)

Introduction

Il y a plusieurs façons d’utiliser le C++. On peut être utilisateur de classes ou concepteur de classes. Dans les deux cas, définir, designer et implémenter des classes sont les activités primaires des développeurs C++. Par où on commence ? En général, on démarre avec une abstraction ou un concept. On considère un truc à coder et puis on essaie de voir comment on va gérer son ou ses fonctions, ce qui est interne et ce qui est public et comment on va l’utiliser… Au démarrage de cette réflexion, il faut se mettre la casquette de celui qui va utiliser la classe. Notre exemple de concept pour cet article sera un élément graphique à dessiner comme une ligne, un rectangle, une ellipse, etc. En anglais, c’est un shape.

// C’est une déclaration de classe

class CShape;

// C’est la future manière avec laquelle je veux dessiner des Shapes

bool Draw(const CShape& item);

Ensuite on envisage la classe dans son ensemble :

class CShape

{

public:

// interface publique

private:

// implémentation privée

};

Qu’est-ce que l’on met en private/public et comment va-t-elle être structurée. On commence petit bras et puis la classe prend du volume.

Constructeur et Destructeur

La première question qui vient à l’esprit est comment je vais créer mon objet et ai-je besoin de lui fournir des paramètres. Avec ma classe CShape, on peut se dire, je n’ai pas besoin mais aussi j’en ai besoin. Les deux possibilités existent ! Soit c’est une figure connue -> d’où une énumération pour les types connus et autre possibilité qui est de dessiner une image et là, il me faut un nom de fichier ; tout est ouvert !

enum ShapeType

{

line,

circle,

rectangle,

left_arrow,

right_arrow,

picture

};

L’avantage de l’énumération es que c’est évalué à la compilation. Il ne peut pas y avoir d’erreur sur le nommage car c’est un type explicite contrairement à une simple chaîne de caractères. Voyons sommairement comment pourrait être construite cette fameuse classe CShape…

class CShape

{

public:

CShape();

CShape(int x, int y, int xx, int yy, ShapeType type);

CShape(int x, int y, int xx, int yy, std::string fileName);

virtual ~CShape();

private:

std::string m_fileName;

ShapeType m_type;

};

J’y vois deux façons de créer un shape. Soit à partir de l’énumération soit à partir d’un fichier pour les images. Pour le moment, je ne sais pas comment organiser le dessin du shape. Dois-je le mettre à l’intérieur de la classe ou à l’extérieur ? Je ne sais pas… Il faut du temps pour considérer quelle sera la façon la plus naturelle de réaliser cette opération. Dans le monde Windows GDI+, pour dessiner un élément il faut un handle particulier qui possède toutes les primitives de dessins. En pensant comment allait être dessiner un élément, on peut envisager d’inclure une méthode Draw dans la classe CShape. La première pensée, c’est de se dire, je vais implémenter tous les cas possibles dans Draw avec une construction qui ressemble à ça :

void Draw(const CDrawingContext& ctxt) const

{

if (m_type == ShapeType::line)

{

ctxt.Line(m_rect.left, m_rect.bottom,

m_rect.top, m_rect.right);

}

if (m_type == ShapeType::rectangle)

{

ctxt.Rectangle(m_rect);

}

// TODO

}

Dans la classe, il est important de marquer les méthodes qui ne modifient pas les données private. Pour faire cela, on leur ajoute le mot-clé const à la fin comme c’est indiqué sur la méthode Draw.

De plus, lorsque l’on passe un argument qui n’as pas vocation à être modifié, on le passe en référence const. Comme on peut le voir ci-dessus, la définition d’une classe est un travail itératif dans lequel, les meilleures idées chassent celles d’avant ou les conforte. Si une classe est bien conçue, le lecture de ses membres public est limpide car on va à l’essentiel. Attention, les membres private expliquent aussi beaucoup de choses sur les détails d’implémentation. Maintenant, mettons-nous dans un contexte où il existe plusieurs classes et les principes vu ci-dessus ne suffisent pas à faire un modèle objet. Il nous faut des mécanismes plus sophistiqués pour relier les classes les unes avec les autres.

Les classes possèdent des associations et des comportements et c’est ici que les Jedi se positionnent pour y designer u nmodèle élégant, facile à utiliser et qui rend les services voulus.

Notre méthode Draw n’est pas OOP car si je rajoute un type dans l’énumération, je dois ajouter un if() dans la méthode Draw() et coder l’implémentation. On peut faire mieux, beaucoup mieux.

Les concepts de OOP

Les deux piliers de l’OOP sont l’héritage et le polymorphisme. L’héritage permet de grouper les classes en famille de types et permet de partager des opérations et des comportements et des données. Le polymorphisme permet de déclencher des opérations dans ces familles à un niveau unitaire. Ainsi ajouter ou supprimer une classe n’est pas trop un problème.

L’héritage définit une relation parent/enfant. Le parent définit l’interface public et l’implémentation private est commune à tous ses enfants. Chaque enfant choisit s’il veut hériter d’un comportement unique ou bien le surcharger. En C++, le parent est appelé « classe de base » et l’enfant « classe dérivée ». Le parent et les enfants définissent une hiérarchie de classes. Le parent est souvent une classe abstraite et les enfants implémentent les méthodes virtuelles pures pour que l’ensemble fonctionne.

Dans un programme OOP, on manipule les classes via un pointeur sur la classe de base plutôt que sur les objets dérivés.

Une classe abstraite

Revenons sur la classe CShape… La prochaine étape dans le design est de créer une classe abstraite pour identifier les opérations propres à chaque item – ce qui implique des opérations avec une implémentation basée sur une classe dérivée. Ces opérations sont des fonctions virtuelles pures de la classe de base. Une méthode virtuelle pure est une méthode abstraite. Il n’y a pas de corps. Cela implique que le classe devient abstraite aussi. Il n’est pas possible de créer un objet à partir d’une classe abstraite. La méthode Draw() doit être définie comme virtuelle pure car il y a plusieurs types de dessin à faire et que chaque dessin est propre à une classe donnée. Le corps de Draw() n’existe pas, c’est la raison pour laquelle on dit que la classe est abstraite ; il y a au moins une méthode virtuelle pure.

Voici donc comment on fait :

class CShapeEx

{

public:

CShapeEx() {}

CShapeEx(ShapeType type, RECT rect, const std::string &fileName)

: m_type(type), m_rect(rect), m_fileName(fileName) {}

virtual ~CShapeEx() {}

public:

virtual void Draw(const CDrawingContext& ctxt) const = 0;

virtual void DrawTracker(const CDrawingContext& ctxt) const = 0;

private:

std::string m_fileName;

ShapeType m_type;

RECT m_rect;

};

Et nous allons maintenant déclarer des classes dérivées de CShapeEx :

class CRectangle : public CShapeEx

{

public:

CRectangle() {}

virtual ~CRectangle() {}

public:

virtual void Draw(const CDrawingContext& ctxt) const

{

// TODO

std::cout << "Rectangle::Draw" << std::endl;

}

virtual void DrawTracker(const CDrawingContext& ctxt) const

{

// TODO

}

};

class CLine : public CShapeEx

{

public:

CLine() {}

virtual ~CLine() {}

public:

virtual void Draw(const CDrawingContext& ctxt) const

{

// TODO

std::cout << "Line ::Draw" << std::endl;

}

virtual void DrawTracker(const CDrawingContext& ctxt) const

{

// TODO

}

};

Si on veut rajouter un item à dessiner dans la hiérarchie, il suffit de rajouter une classe dérivée ! Maintenant nous allons voir comment fonctionne ce mécanisme de virtual…

CDrawingContext ctxt;

CRectangle * pRect = new CRectangle();

CLine * pLine = new CLine();

CShapeEx * ptr = nullptr;

ptr = pRect;

ptr->Draw(ctxt);

ptr = pLine;

ptr->Draw(ctxt);

On commence par déclarer 2 items à dessiner. Puis le manager, un pointeur sur la classe de base CShapeEx est déclaré. A chaque fois qu’il pointe sur un objet d’une classe dérivée, la fonction virtuelle Draw() appelée est celle de la classe dérivée car cette fonction est définie comme virtuelle.

Maintenant, il nous manque quelque chose de très utile et qui évitera les usines à gaz de construction d’objets dérivés. Il nous faut une factory ! Un mécanisme de création d’objet.

La factory

class CFactory

{

private:

CFactory();

public:

static CShapeEx * CreateObject(ShapeType type)

{

if (type == ShapeType::line)

{

CLine * pObj = new CLine();

pObj->m_type = type;

return pObj;

}

if (type == ShapeType::rectange)

{

CRectangle * pObj = new CRectangle();

pObj->m_type = type;

return pObj;

}

// TODO

return nullptr;

}

};

Cette classe permet via une méthode static de créer un objet en fonction de son type dans l’énumération. Le type est affecté en variable membre de l’objet créé. On notera que le constructeur est private, cela veut dire que l’on ne peut pas déclarer d’objet CFactory. On ne peut qu’utiliser la méthode static CreateObject. Sauf que, la compilation tombe en erreur :

Le membre m_type est inaccessible vue son niveau de protection… On va donc ajouter quelque chose à la classe CShapeEx :

class CShapeEx

{

public:

CShapeEx() {}

CShapeEx(ShapeType type, RECT rect, const std::string &fileName)

: m_type(type), m_rect(rect), m_fileName(fileName) {}

virtual ~CShapeEx() {}

public:

virtual void Draw(const CDrawingContext& ctxt) const = 0;

virtual void DrawTracker(const CDrawingContext& ctxt) const = 0;

private:

std::string m_fileName;

ShapeType m_type;

RECT m_rect;

// La classe CFactory peux avoir accès aux membres…

friend class CFactory;

};

On ajoute en fin de classe que la classe CFactory est amie de la classe CShapeEx. Et là, il n’y a plus d’erreur de compilation ! Les puristes comme AlainZ, à Redmond, vous diront que cela viole la mécanique objet et que c’est une construction exotique et qu’il vaut mieux passer par le constructeur pour passer le type… Je ne suis pas de cet avis. Je pense que c’est élégant de faire une entorse pour la factory. Voici maintenant le code qui appelle la factory :

CDrawingContext ctxt;

CShapeEx * pRect = CFactory::CreateObject(ShapeType::rectange);

CShapeEx * pLine = CFactory::CreateObject(ShapeType::line);

CShapeEx * ptr = nullptr;

ptr = pRect;

ptr->Draw(ctxt);

ptr = pLine;

ptr->Draw(ctxt);

Et le résultat est équivalent.

Compilation sous Linux

Pour ceux qui ne le savent pas, le langage C++ est normalisé ISO. Sur la plateforme Linux, là où tout est fait en C/C++, le langage possède plusieurs compilateurs dont le célèbre GCC. Vous pouvez utiliser Visual Studio Code que fournit Microsoft comme IDE pour compiler sous Linux via le terminal.

Il faut installer l’extension C++. Moi j’ai installé Cygwin et MSYS. Ainsi je dispose de commandes Linux.

Une touche de C++ 11

On va essayer de continuer notre application de dessin en matérialisant la zone de dessiner. Cela ressemble à un ensemble de shapes sur un support de dessin. Première étape quand on pense au concept de la planche à dessin c’est quoi ? Le stockage des éléments graphiques dans un container STL. Oui Monsieur, quand on veut stocker quelque chose, on passe par les containers STL. On va donc utiliser le std::vector<T> de la STL. On va stocker des shapes un peu particulier que sont les shapes partagés. Partagés ? Par qui ? Dans un vrai logiciel de dessin, on affiche les propriétés dans une grille et le dessin est sur le panneau central donc on partage nos objets et ce n’est pas que de l’affichage. Je vous montre le résultat.

Sur cet écran, on voit que le rectangle bleu sélectionné a ses attributs directement dans le grille de propriétés mais aussi sur le Ribbon. Comment faire pour partager les données entre le panneau de dessin, le Ribbon et la grille de propriétés ? Est-ce que l’on fait des copies des objets ? Oui, mais on fait des copies de pointeurs et non des copies d’objets. Pour faire cela, une utilise le mécanisme des pointeurs partagés de la STL. Voyons son utilisation. Premièrement, on va designer la zone de dessin.

La zone de dessin

Cette zone contient les objets shape à dessiner. On va donc créer un vector<shared_ptr<CShapeEx>>. Le container vector est défini dans <vector>. On va avoir une collection d’éléments partagés CShapeEx. Ainsi, on pourra stocker ce pointeur particulier où on veut sans se soucier de la libération de la mémoire. La zone de dessin est une vue fille MDI en jargon Windows. Elle possède deux scrollbars, un verticale et une horizontale. Si on veut faire une gestion de shared_ptr<T>, il faut faire évoluer la factory :

static std::shared_ptr<CShapeEx> CreateObject(ShapeType type)

{

std::shared_ptr<CShapeEx> pObj = nullptr;

if (type == ShapeType::line)

{

pObj = std::make_shared<CLine>();

pObj->m_type = type;

return pObj;

}

if (type == ShapeType::rectangle)

{

pObj = std::make_shared<CRectangle>();

pObj->m_type = type;

return pObj;

}

return nullptr;

}

On ne fait plus un new() mais un appel à std::make_shared<T>().

De plus, dans l’utilisation des objets, cela donne ça :

CDrawingContext ctxt;

std::shared_ptr<CShapeEx> pRect = CFactory::CreateObject(ShapeType::rectangle);

std::shared_ptr<CShapeEx> pLine = CFactory::CreateObject(ShapeType::line);

std::shared_ptr<CShapeEx> ptr = nullptr;

ptr = pRect;

ptr->Draw(ctxt);

ptr = pLine;

ptr->Draw(ctxt);

Il existe même une façon de laisser le compilateur déterminer le type de variable utilisée en utilisant auto :

CDrawingContext ctxt;

auto pRect = CFactory::CreateObject(ShapeType::rectangle);

auto pLine = CFactory::CreateObject(ShapeType::line);

auto ptr = pRect;

ptr->Draw(ctxt);

ptr = pLine;

ptr->Draw(ctxt);

Revenons à notre design de classes. Le container de shapes est juste une collection std::vector<T> de std::shared_ptr<CShapeEx> :

class CElementContainer : public CObject

{

private:

public:

DECLARE_SERIAL(CElementContainer);

CElementContainer();

virtual ~CElementContainer(void);

// ../..

// overridden for document i/o

virtual void Serialize(CElementManager * pElementManager, CArchive& ar);

// Debuging Operations

public:

void DebugDumpObjects(CModeler1View * pView);

// Operations MFC like

public:

std::shared_ptr<CElement> GetHead();

int GetCount();

void RemoveAll();

void Remove(std::shared_ptr<CElement> pElement);

void AddTail(std::shared_ptr<CElement> pElement);

// Attributes

private:

vector<std::shared_ptr<CElement>> m_objects;

};

Là, je vous montre un vrai code et les éléments sont matérialisés par la classe CElement mais c’est pareil que CShapeEx. On notera que les fonctions de gestion associées au vector sont des méthodes helpers dans cette classe. Il faut maintenant intégrer cette classe dans la mécanique de fenêtrage Windows.

Je pratique le C++ – Partie 5/5 – Boost

Je pratique le C++ – Partie 5/5.

Nous vous proposons une série d’articles sur la pratique de C++ pour que vous puissiez tous vous y mettre. Ce mois-ci on aborde la librairie Boost du site www.boost.org. Boost est une communauté de programmeurs C++ dont le but est de pousser les librairies qu’ils font vers la standardisation C++. Boost c’est une librairie C++ portable. Vous pouvez l’utiliser avec Visual C++ sur Windows et GCC ou CLang sur Linux.

Comment obtenir Boost ?

Très simplement en allant dans la rubrique download du site et vous êtes et vous télécharger un zip de 120 MB ou un targz de 80 MB. Vous décompressez votre archive et vous êtes presque prêt. La décompression vous donne une arborescence de 430 MB. Il y a énormément de documentation HTML c’est la raison pour laquelle la taille du dossier est conséquente.

 

Compilation de Boost

Malgré que Boost soit en majorité un ensemble de templates et de fonctions inline donc juste les fichiers d’entêtes suffisent, il y a des librairies qu’il faut compiler.

La première chose à faire c’est de lancer la compilation de la librairie. Cela se fait en deux étapes. Premièrement, il faut compiler le custom Make de Boost qui se nomme bjam. Il faut lancer bootstrap.bat. Maintenant que bjam est compilé, on va builder la librairie avec notre chaine de compilation particulière à une plateforme. Je travaille avec Visual Studio 2013 donc voici ma ligne de commande :

 

bjam toolset=msvc-12.0 variant=debug,release threading=multi link=shared

 

Je fais une compilation en debug et en relase et indiquant que je veux une librairie multi-thread et en mode dynamique (DLL). La compilation prends un peu de temps mais disons qu’en 15 minutes c’est terminé.

 

 

Introduction à Boost.Serialization

Maintenant que nous avons listé l’ensemble des librairies disponibles dans Boost, nous allons passer au code ! Examinons une librairie très utile qui sait sérialiser et dé-sérialiser des classes en format binaire ou XML sans forcer. Cette librairie permet de sérialiser des containers ce qui évitera de parcourir nos différentes collections pour sérialiser des éléments simples. Par contre, nous devons faire attention à une collision de nom dans l’espace de noms disponible. Je m’explique… Boost sérialise les boost ::string, les boost ::vector & co… Ce qui veut dire qu’il va falloir transvaser nos objets écrits avec la STL standard dans des objets Boost compatibles à moins que notre application soit écrite complètement avec Boost. C’est un choix d’Architecture !

Nous allons partir sur une application que j’ai écrite pour ma fille en 2012. Il s’agit d’une application de dessin ou l’on dispose des éléments prédéfinis sur une page comme des images, des rectangles, des ronds, des triangles, etc. Cette application a permit à ma fille de 8 ans à l’époque de savoir manier la souris et de jouer avec le Ribbon pour changer les caractéristiques des objets (couleur, épaisseur de traits, etc).

 

Le résultat de la sérialisation XML

Commençons par la fin… Le dessin est le suivant :

Le but du jeu est de sauvegarder ce dessin sous forme de document XML.

Ouvrons la documentation de Boost.Serialization pour prendre la chose du bon côté et pour arriver au résultat ci-dessus. Si vous avez l’œil, vous verrez que l’application que je vous propose est réalisée avec les Microsoft Foundation Classes (MFC) car elle me permet de faire un joli Ribbon et de mettre en place le support du Document/Vue qui me permet de faire une application qui charge/enregistre mes données. Bref, MFC rocks. Je crois d’ailleurs qu’on va refaire une série Je Pratique le C++ sous Windows en 5 épisodes pour que vous puissiez développer vous aussi des applications qui rocks ! On verra… revenons à nos moutons. Le XML. Boost.Serialization fonctionne avec ce que l’on appelle une Archive dans laquelle on a accès à des données. Il faut nourrir l’archive avec des données. J’ai donc une classe d’objets à dessiner et des objets. Le modèle de données est constitué de 2 classes : CSimpleShape et CShapeCollection.

 

La classe CSimpleShape

Cette classe contient les propriétés suivantes :

public:

wstring m_name;

wstring m_id;

string m_rect;

long m_type;

long m_shapeType;

wstring m_caption;

wstring m_text;

long m_x1;

long m_y1;

long m_x2;

long m_y2;

int m_colorFillR;

int m_colorFillG;

int m_colorFillB;

int m_colorLineR;

int m_colorLineG;

int m_colorLineB;

bool m_bSolidColorFill;

bool m_bColorLine;

bool m_bColorFill;

};

 

BOOST_CLASS_VERSION(CSimpleShape, 1)

 

La classe CShapeCollection

Cette classe contient la liste des elements:

public:

vector<boost::shared_ptr<CSimpleShape> > m_shapes;

};

 

BOOST_CLASS_VERSION(CShapeCollection, 1)

 

C’est un vecteur de CSimpleShape tout simplement… Pour que ces éléments soient stockés sous forme de fichier texte, binaire ou XML il faut préciser quelques petites choses dans les classes. Il faut spécifier cela en private :

private:

friend class boost::serialization::access;

template<class Archive>

void save(Archive & ar, const unsigned int version) const

{

ar & BOOST_SERIALIZATION_NVP(m_shapes);

}

 

template<class Archive>

void load(Archive & ar, const unsigned int version)

{

ar & BOOST_SERIALIZATION_NVP(m_shapes);

}

 

BOOST_SERIALIZATION_SPLIT_MEMBER()

 

Cette partie de code nous montre le template à définir. Il s’agit d’une fonction template load et save qui travaille sur une Archive. La syntaxe pour enroler une donnée dans l’archive se fait au travers de l’opérateur &. Cette partie de code enregistre le vecteur d’éléments. Mais pour que cela fonctionne, il faut aussi que le classe CSimpleShape enregistre ses éléments dans l’archive.

template<class Archive>

void save(Archive & ar, const unsigned int version) const

{

// ar & name;

// ar & id;

ar & BOOST_SERIALIZATION_NVP(m_name);

ar & BOOST_SERIALIZATION_NVP(m_id);

ar & BOOST_SERIALIZATION_NVP(m_rect);

ar & BOOST_SERIALIZATION_NVP(m_type);

ar & BOOST_SERIALIZATION_NVP(m_shapeType);

ar & BOOST_SERIALIZATION_NVP(m_caption);

ar & BOOST_SERIALIZATION_NVP(m_text);

ar & BOOST_SERIALIZATION_NVP(m_x1);

ar & BOOST_SERIALIZATION_NVP(m_y1);

ar & BOOST_SERIALIZATION_NVP(m_x2);

ar & BOOST_SERIALIZATION_NVP(m_y2);

ar & BOOST_SERIALIZATION_NVP(m_colorFillR);

ar & BOOST_SERIALIZATION_NVP(m_colorFillG);

ar & BOOST_SERIALIZATION_NVP(m_colorFillB);

ar & BOOST_SERIALIZATION_NVP(m_colorLineR);

ar & BOOST_SERIALIZATION_NVP(m_colorLineG);

ar & BOOST_SERIALIZATION_NVP(m_colorLineB);

ar & BOOST_SERIALIZATION_NVP(m_bSolidColorFill);

ar & BOOST_SERIALIZATION_NVP(m_bColorLine);

ar & BOOST_SERIALIZATION_NVP(m_bColorFill);

}

 

Le code du load et du save est le même. L’archive sait dans quel sens on travaille soit en lecture soit en écriture. Il faut noter que les MFC fournissent une infrastructure similaire avec les opérateurs << et >> ; je trouve que c’est plus lisible à mon goût mais bon. Pour pouvoir compiler ce code, il faut disposer des entêtes suivante :

#define BOOST_SERIALIZATION_DYN_LINK TRUE

#define BOOST_ALL_DYN_LINK TRUE

 

#include <boost/smart_ptr/shared_ptr.hpp>

#include <boost/archive/tmpdir.hpp>

#include <boost/serialization/nvp.hpp>

 

#include <boost/archive/xml_iarchive.hpp>

#include <boost/archive/xml_oarchive.hpp>

#include <boost/archive/text_iarchive.hpp>

#include <boost/archive/text_oarchive.hpp>

#include <boost/serialization/shared_ptr.hpp>

#include <boost/serialization/base_object.hpp>

#include <boost/serialization/string.hpp>

#include <boost/serialization/list.hpp>

#include <boost/serialization/vector.hpp>

#include <boost/serialization/map.hpp>

#include <boost/serialization/utility.hpp>

#include <boost/serialization/assume_abstract.hpp>

using namespace boost;

 

L’écriture des données

Maintenant que nous avons nos classes qui savent utiliser une Archive, voyons le code qui donne l’ordre de faire Load ou Save. Vous allez voir, c’est très simple :

boost::shared_ptr<CShapeCollection> data(new CShapeCollection());

for( vector<std::shared_ptr<CElement>>::iterator i = m_objects.m_objects.begin() ; i!=m_objects.m_objects.end() ; i++ )

{

std::shared_ptr<CElement> pElement = *i;

boost::shared_ptr<CSimpleShape> pNewElement(new CSimpleShape());

pNewElement->m_name = pElement->m_name;

pNewElement->m_id = pElement->m_objectId;

pNewElement->m_type = pElement->m_type;

pNewElement->m_shapeType = pElement->m_shapeType;

pNewElement->m_caption = pElement->m_caption;

pNewElement->m_text = pElement->m_text;

 

CPoint p1 = pElement->m_rect.TopLeft();

CPoint p2 = pElement->m_rect.BottomRight();

pNewElement->m_x1 = p1.x;

pNewElement->m_y1 = p1.y;

pNewElement->m_x2 = p2.x;

pNewElement->m_y2 = p2.y;

 

pNewElement->m_colorFillR = GetRValue(pElement->m_colorFill);

pNewElement->m_colorFillG = GetGValue(pElement->m_colorFill);

pNewElement->m_colorFillB = GetBValue(pElement->m_colorFill);

pNewElement->m_colorLineR = GetRValue(pElement->m_colorLine);

pNewElement->m_colorLineG = GetGValue(pElement->m_colorLine);

pNewElement->m_colorLineB = GetBValue(pElement->m_colorLine);

 

pNewElement->m_bSolidColorFill = pElement->m_bSolidColorFill;

pNewElement->m_bColorLine = pElement->m_bColorLine;

pNewElement->m_bColorFill = pElement->m_bColorFill;

 

data->m_shapes.push_back(pNewElement);

}

 

std::ofstream xofs(filename.c_str());

boost::archive::xml_oarchive xoa(xofs);

xoa << BOOST_SERIALIZATION_NVP(data);

 

La variable filename est remplie par l’ouverture d’une Common Dialog Windows SaveAs et toute la magie consiste à déclarer une xml_archive et à utiliser l’opérateur << pour sauver toutes nos données.

 

La lecture des données

La lecture des données est calquée sur l’écriture, on récupère les données et on les transvase dans la collection qui va s’afficher à l’écran :

 

boost::shared_ptr<CShapeCollection> data(new CShapeCollection());

// load an archive

std::ifstream xifs(filename.c_str());

assert(xifs.good());

boost::archive::xml_iarchive xia(xifs);

xia >> BOOST_SERIALIZATION_NVP(data);

// Clear existing shapes

m_objects.RemoveAll();

 

for( vector<boost::shared_ptr<CSimpleShape> >::iterator i = data->m_shapes.begin() ; i!=data->m_shapes.end() ; i++ )

{

boost::shared_ptr<CSimpleShape> pElement = *i;

//AfxMessageBox(pElement->m_name + ” ” + pElement->m_id);

 

std::shared_ptr<CElement> pNewElement = CFactory::CreateElementOfType((ElementType)pElement->m_type,

(ShapeType)pElement->m_shapeType);

pNewElement->m_name = pElement->m_name.c_str();

pNewElement->m_objectId = pElement->m_id.c_str();

pNewElement->m_caption = pElement->m_caption.c_str();

pNewElement->m_text = pElement->m_text.c_str();

pNewElement->m_pManager = this;

pNewElement->m_pView = pView;

 

CPoint p1;

CPoint p2;

p1.x = pElement->m_x1;

p1.y = pElement->m_y1;

p2.x = pElement->m_x2;

p2.y = pElement->m_y2;

pNewElement->m_rect = CRect(p1, p2);

 

int colorFillR = pElement->m_colorFillR;

int colorFillG = pElement->m_colorFillG;

int colorFillB = pElement->m_colorFillB;

pNewElement->m_colorFill = RGB(colorFillR, colorFillG, colorFillB);

int colorLineR = pElement->m_colorLineR;

int colorLineG = pElement->m_colorLineG;

int colorLineB = pElement->m_colorLineB;

pNewElement->m_colorLine = RGB(colorLineR, colorLineG, colorLineB);

 

pNewElement->m_bSolidColorFill = pElement->m_bSolidColorFill;

pNewElement->m_bColorLine = pElement->m_bColorLine;

pNewElement->m_bColorFill = pElement->m_bColorFill;

 

m_objects.AddTail(pNewElement);

pView->LogDebug(_T(“object created ->”) + pNewElement->ToString());

}

 

// Redraw the view

Invalidate(pView);

 

Conclusion

L’utilisation de Boost et de Boost.Serialization est plutôt simple à appréhender. La documentation fournie est de bonne facture et les exemples sont légions dans la documentation. L’avantage de Boost.Serialization est que les archives sont portables… Donc pour faire du multi-plateforme, c’est easy ! Le code complet de l’application de dessin est disponible ici : http://ultrafluid.codeplex.com .

 

Conclusion sur la série « Je Pratique C++ »

Avec cet article, la série « Je pratique le C++ » s’arrête mais je vais trouver un deal avec Programmez pour que nous ayons toujours du C++ dans Programmez. Tous les logiciels Microsoft sont faits en C/C++ à 90% et il ne serait pas normal de ne pas en parler. Le C++ n’est pas mort, loin de la. Avec C++11, le C++ à rajeuni et les standards C++14 et C++17 apportent leur lot de nouveautés. Stay tuned comme dirait l’autre et à Bientôt pour de nouveaux articles sur le C++.

Je pratique le C++ – Partie 4/5 – la STL

Je pratique le C++ – Partie 4/5.

Nous vous proposons une série d’articles sur la pratique de C++ pour que vous puissiez tous vous y mettre. Ce mois-ci on aborde la librairie standard du C++ nommé STL : Standard Template Library.

Le langage C possède sa runtime et le C++ possède aussi sa runtime, appelée STL. Dans le monde Microsoft, la runtime du C se nomme MSVCRTxxx.dll et celle du C++ se nomme MSVCPxxx.dll. Cependant, la STL ne se comporte pas comme une liste de fonctions que l’on peut appeler – exemple fopen en C. La STL est constituée de templates. SI vous avez lu l’article précédent 3/5, nous y avons étudié les templates en douceur. Cela veut dire plusieurs choses. La STL est organisée via des fichiers d’entêtes.

Les fonctionnalités offertes par la bibliothèque standard peuvent être classées comme suit :

• Support du run-time du langage (par exemple, pour l’allocation et les informations de type run-time).

• La bibliothèque C standard (avec des modifications vraiment mineures pour minimiser la violation du système de type).

• Les chaînes (avec le support des jeux de caractères internationaux et localisation).

• Support pour la correspondance des expressions régulières.

• Les flux d’entrée/sortie sont un framework extensible pour l’entrée et la sortie vers lesquels les utilisateurs peuvent ajouter leurs propres types, flux, stratégies de buffering, locales et jeux de caractères.

• Un framework de conteneurs (comme vector et map) et d’algorithmes (comme find(), sort(), et merge()). Ce framework, appelé conventionnellement la STL, est extensible pour que les utilisateurs puissent y ajouter leurs propres conteneurs et algorithmes.

• Support pour le calcul numérique (comme les fonctions mathématique standard, les nombres complexes, les vecteurs avec des opérations arithmétiques, et les générateurs de nombres aléatoires).

• Support pour la programmation parallèle, incluant les threads et les verrous. Le support parallèle est fondamental pour les utilisateurs puissent ajouter le support aux nouveaux modèles parallèles dans des bibliothèques.

• Des classes utilitaires pour le support de la méta programmation à base de templates (par exemple, type traits, la programmation générique avec le style STL (par exemple, pair), et la programmation générale (par exemple, clock).

• Les ‘‘Pointeurs Intelligent’’ pour la gestion des ressources (par exemple, unique_ptr et shared_ptr) et une interface pour la libération des ressources.

• Des conteneurs spéciaux, comme array, bitset, et tuple.

Le critère principal pour inclure une classe dans la bibliothèque étaient que:

• cela pourrait être utile à presque tous les programmeurs C++ (les novices ou les experts)

• cela pourrait être fournie sous une forme générale qui n’ajoute pas de surcharge importante par rapport à une version simplifiée de la même fonctionnalité, et

• que l’utilisation simple doit être simple à apprendre (par rapport à la complexité de la tâche).

La bibliothèque standard de C++ fournit essentiellement les structures de données fondamentales les plus courantes ainsi que les algorithmes fondamentaux à utiliser avec. Nous allons balayer tout ou partie de cette liste exhaustive.

Sélection des fichiers d’entêtes de la bibliothèque standard:
<algorithm> copy(), find(), sort()
<array> array
<chrono> duration, time_point
<cmath> sqrt(), pow()
<complex> complex, sqrt(), pow()
<forward_list> forward_list
<fstream> fstream, ifstream, ofstream
<future> future, promise
<ios> hex,dec,scientific,fixed,defaultfloat
<iostream> istream, ostream, cin, cout
<map> map, multimap
<memory> unique_ptr, shared_ptr, allocator
<random> default_random_engine, normal_distribution
<regex> regex, smatch
<string> string, basic_string
<set> set, multiset
<sstream> istrstream, ostrstream
<stdexcept> length_error, out_of_rang e, runtime_error
<thread> thread
<unordered_map> unordered_map, unordered_multimap
<utility> move(), swap(), pair
<vector> vector

Concepts généraux

La STL utilise l’espace de nom (namespace) std. Il est possible de s’en passer en faisant un using :

using namespace std;

Les fichiers d’entêtes de la STL sont nommés sans l’extension .h, exemple :

#include <iostream>

#include <sstream>

#include <fstream>

#include <string>

#include <vector>

#include <list>

#include <map>

#include <memory>

#include <array>

#include <utility>

#include <thread>

#include <algorithm>

using namespace std;

Le type string

Ce type est défini dans le fichier d’entête <string> et c’est le type qui permet de faire la liaison entre les type char du C et le type string d’un niveau plus haut qu’est le type string. Le type string permet de concaténer des string entre elles. Ce n’est pas un pointeur de char. C’est un template.

typedef basic_string<char, char_traits<char>, allocator<char> >

string;

Il est possible de fournir son propre allocateur mémoire. Mais qui va utilisez cela ? Et pourtant c’est prévu… Rappelez-vous dans l’article sur les classes lorsque je parlais de la casquette auteur de classe et de la casquette utilisateur de classe. On y est exactement sauf que c’est une classe template.

string msg = “Le nutty est coquine !”;

string msg2 = “Maggie est la reine des coquines !”;

string s = msg + ” ” + msg2;

cout << s << endl;

string nutty = s.substr(0, 9);

cout << nutty << endl;

msg[3] = toupper(msg[3]);

cout << msg << endl;

string temp(“VS 2015 is here !”);

size_t t = temp.find_first_of(“here”);

cout << temp << ” ” << t << endl;

La classe string contient de nombreuses opérations qui évitent le parcours des chaines C à l’ancienne. Pour passer d’une string à un type C char *, il faut utiliser la fonction c_str() :

const char * psz = msg.c_str();

printf(“String const char * = %sn”, psz);

Les expressions régulières (regex)

La fonctionnalité des regex est disponible dans le fichier d’entête <regex>. Le support est le même que dans d’autres langages ; on y trouve les fonctions regex_match, regex_search, regex_replace, regex_iterator, regex_token_ietrator.

Les IO/Streams

La STL utilise les flux pour gérer des I/O avec des buffers. La fonction la plus connue est cout et son opérateur ‘<<’.

cout << “On n’est pas que des Mickey !” << endl;

int i = 10;

float f = 2.5;

cout << i << “, ” << f << endl;

Le meilleur ami de cout est cin. Cela permet de capter les entrées du clavier :

int j = 0;

cout << “Give ma a Int !” << endl;

cin >> j;

cout << “Merci pour ” << j << endl;

En plus de gérer les types standards et les string, il est possible de tirer parti de la librairie pour afficher d’autres types. Exemple, considérons le type suivant :

struct CPersonne

{

int age;

string name;

};

ostream& operator<<(ostream& os, const CPersonne& p)

{

return os << p.name << ” ” << p.age << ” ans”;

}

void TestIO()

{

CPersonne p;

p.age = 5;

p.name = “Audrey Maggie”;

cout << “La soeur de Lisa c’est ” << p << endl;

}

La définition de l’opérateur << avec le type ostream permet de passer un type CPersonne à cout et cela sans forcer ! Autres aspects de la librairie I/O, c’est le formatage. Pour afficher des types comme le fait une fonction printf en C avec les %d, %f, %x et %s ont leur équivalent dans la librairie.

void TestFormating()

{

float f = 2.50;

cout << f << “;”

<< scientific << f << “;”

<< hexfloat << f << “;”

<< fixed << f << “;”

<< defaultfloat << f << endl;

}

La gestion des fichiers est assurée au travers du fichier d’entêtes <fstream> :

  • ifstream permet de lire un fichier
  • ofstream permet d’écrire un fichier
  • fstream permet de lire et d’écrire dans un fichier

Exemple d’écriture de fichier :

ofstream ofs(“c:\temp\MyGirls.txt”);

ofs << “Edith” << endl;

ofs << “Lisa” << endl;

ofs << “Maggie” << endl;

ofs.close();

C’est vraiment très simple. La librairie I/O permet de gérer les buffers. Le fichier d’entête <sstream> permet de manipuler des chaînes :

  • istringstream pour lire des chaînes
  • ostringstream pour écrire des chaînes
  • stringstream pour lire et écrire des chaînes

int i = 20;

float f = 5.75;

string s = “Maggie est trop coquine !”;

ostringstream oss;

oss << i << “, ” << f << “, ” << s;

string str = oss.str();

cout << str << endl;

Les containers

Le container le plus utilisé est vector<T>. Il est disponible dans le fichier d’entête <vector>. C’est une séquence d’éléments d’un type donné. Les éléments sont stockés de manière contiguë. Pour ajouter des éléments à un vector, il faut utiliser la méthode push_back(). Le parcours d’un vector peut se faire avec un range-for ou bien en utilisant un itérateur. Les containers de la STL sont tous accessibles au travers un itérateur. Les fonctions begin(), end(), operator++, operator—m operator* permettent de manipuler un itérateur. Exemple :

vector<CPersonne> v = { {12, “Edith”}, {9, “Lisa”}, {5, “Maggie”} };

CPersonne p;

p.age = 41;

p.name = “Papa”;

v.push_back(p);

for (auto item : v)

{

cout << item.age << ” ” << item.name << endl;

}

for (vector<CPersonne>::const_iterator it = begin(v); it != end(v); ++it)

{

CPersonne p = *it;

cout << p.age << ” ” << p.name << endl;

}

Voici la liste des opérations principales pour vector<T> :

Opération Explication
v.empty() Retourne true si v est vide. Sinon retourne false.
v.size() Retourne le numbre d’élements dans v.
v.push_back(t) Ajoute un élément de valeur t à la fin de v.
v[n] Retourne une référence vers l’élement en position n dans v.
v1=v2 Remplace les éléments dans v1 avec une copie des éléments dans v2.
v1={a, b, c…} Remplace les éléments dans v1 avec une copie des éléments de la liste.
v1==v2 v1 et v2 sont égaux si il ya le même nombre d’élements et de valeur.
v1!=v2 opposé de v1==v2
<, <=, >, >= Suivant l’ordre des valeurs retourne un bool

Il existe un container qui représente une double liste chaînées au travers de list. Il est disponible au travers le fichier d’entête <list>.

list<CPersonne> l = { { 12, “Edith” },{ 9, “Lisa” },{ 5, “Maggie” } };

CPersonne p = { 41, “Papa” };

l.push_front(p);

CPersonne p2 = { 40, “Maman” };

l.push_back(p2);

for (auto item : l)

{

cout << item.age << ” ” << item.name << endl;

}

Le container map<K,V> est très utile. Il est disponible dans le fichier d’entête <map>. C’est un container associatif.

map<string, int> family = { { “Edith”, 12 },{ “Lisa”, 9 },{ “Maggie”, 5 } };

family[“Papa”] = 41;

for (map<string, int>::const_iterator it = begin(family); it != end(family); ++it)

{

string name = it->first;

int age = it->second;

cout << name << ” ” << age << endl;

}

Il existe d’autres containers dans la STL comme la hashtable nommée unordered_map. Le container hashtable et ses dérivées (voir tableau ci-dessous) ne contiennent pas le terme hashtable pour des soucis de nommage. Il y a surement du code existant qui ont fait une classe ou un temlplate hashtable et c’est pour éviter la collision de nom.

Les containers disponibles dans la STL
vector<T> un vecteur de taille variable
list<T> une liste doublement chaînée
forward_list<T> une liste chaînée
deque<T> une queue
set<T> un set (une map avec une clé sans valeur)
multiset<T> un set qui peut être en doublon
map<K,V> un tableau associative
multimap<K,V> une map avec une clé qui peut être en doublon
unordered_map<K,V> une map qui utilise un lookup hashtable
unordered_multimap<K,V> une multimap qui utilise un lookup hashtable
unordered_set<K,V> un set qui utilise un lookup hashtable
unordered_multiset<K,V> un multiset qui utilise un lookup hashtable

Les algorithmes

La STL fournit des fonctions simple pour parcourir des ensembles, faire des copies, des insertions, des suppressions, des recherches simple ou complexes. Le fichier d’entête est <algorithm>. La force des algorithmes réside dans le fait qu’elles prennent pour la plupart un itérateur de début et un itérateur de fin afin de réaliser le parcours sur un ensemble fini. C’est un peu déroutant au début et puis finalement, on s’aperçoit que la plupart des parcours proposé dans ce fichier d’entêtes sont bien fait. Il faut maitriser les itérateurs : c’est la seule contrainte. Dans le tableau ci-dessous, b vaut begin() et e vaut end().

Sélection d’algorithmes dans la STL
p=find(b,e ,x) p est le premier p dans [b:e) de telle manière que
p==x

p=find_if(b,e ,f)

p est le premier p dans [b:e) de telle manière que f(p)==true

n=count(b,e ,x)

n est le nombre d’éléments
q dans [b:e) de telle manière
que
q==x

n=count_if(b,e ,f)

n est le nombre d’éléments
q dans [b:e) de telle manière
que f(
q,x)

replace(b,e ,v,v2)

Remplace les éléments
q dans [b:e) de telle manière
que
q==v par v2

replace_if(b,e ,f,v2)

Remplace les éléments
q dans [b:e) de telle manière
que f(
q) par v2

p=copy(b,e ,out)

Copie [b:e) dans [out:p)

p=copy_if(b,e ,out,f)

Copie les éléments
q de [b:e) de telle manière
que f(
q) jusqu’a [out:p)

p=move(b,e ,out)

Déplace [b:e) vers [out:p)

p=unique_copy(b,e ,out)

Copie [b:e) vers [out:p); ne copie pas les duplicates adjacent

sort(b,e)

Tri les éléments de [b:e) en utilisant < comme critère de tri

sort(b,e,f)

Tri les éléments de [b:e) en utilisant la fonction de tri f

(p1,p2)=equal_range(b,e ,v)

[p1:p2) est la subséquence de tri [b:e) avec la valeur v; un binary search de v

p=merge(b,e ,b2,e2,out)

Merge deux séquences [b:e) et [b2:e2) dans [out:p)

Exemple avec find :


vector<string>
v = {
“Edith”,
“Lisa”,
“Maggie” };


//auto res = find(begin(v), end(v), “Maggie”);


vector<string>::iterator
res = find(begin(v), end(v),
“Maggie”);


if (res
== end(v))

{

cout
<<
“Not found !”
<< endl;

}


else

{

cout
<<
“Found ! “
<<
*res
<< endl;


}

Les templates utilities

Tout dans la STL n’est pas aussi simple que les containers ou la librairie I/O stream. Il existe des classes templates qui permettent de tirer parti de fonctionnalités avancées.

Considérons la gestion des ressources, il existe deux templates que sont unique_ptr<T> et share_ptr<T>. unique_ptr<T> représente la possession unique. shared_ptr<T> représente la possession partagée.
Disponible dans le fichier d’entête <memory>, ces « smart pointers » ou pointeurs intelligents permettent de ne plus coder le delete, c’est le template qui s’en occupe. Le principal avantage d’utiliser les smart pointers est d’éviter les fuites mémoire.


unique_ptr<CPersonne>
ptr(
new
CPersonne());


ptr->age
= 41;

ptr->name
=
“Itchy”;


// use ptr


// delete fait automatiquement

Voici la liste des opérations communes entre unique_ptr<T> et shared_ptr<T> :

Opération

Explication

shared_ptr<T> sp

Smart pointer null qui pointe sur un objet T

unique_ptr<T> up

Smart pointer null qui pointe sur un objet T

p

Utilise p comme une condition; true si p pointe sur un objet

*p

Déreference p pour obtenir l’objet sur lequel p pointe

p->mem

Synonyme pour (*p).mem

p.get()

Retourne le pointeur dans p.

swap(p,q)

Swap les pointeurs dans p et q

p.swap(q)

Swap les pointeurs dans p et q

Le template share_ptr<T> possède quelques subtilités :

Opération

Explication

make_shared<T>(args)

Retourne un shared_ptr sur la mémoire allouée et initialise l’objet via args

shared_ptr<T> p(q)

p est une copie de q. Incrémente le compteur de référence interne

p=q

Incrémente le compteur de référence de q

p.use_count()

Retourne le nombre d’objets partagés avec p

p.unique()

Retourne true si p.use_count vaut 1 sinon false

Le template array<T, C> permet de gérer les tableaux aussi rapidement que les built-in arrays.


array<string,
3> ar;

ar[0] = “Edith”;

ar[1] = “Lisa”;

ar[2] = “Audrey”;

for (auto element : ar)

{

cout << element << endl;

}

Le template pair<T, U> représente deux éléments et est disponible dans le fichier d’entête <utility>. Utilisez make_pair pour remplir l’objet pair.

pair<string, float> p;

p.first = “The C++ Object Model”;

p.second = 50.0;

cout << p.first << ” ” << p.second << endl;

pair<string, string> p2 = make_pair(“Maggy”, “t’es une coquine !”);

cout << p2.first << ” ” << p2.second << endl;

Le template tuple<T…> représente une séquence de types variés et de types différents. Utilisez make_tuple pour remplir l’object tuple.

tuple<string, float, string> t;

t = make_tuple(“C++ Primer”, 50.0, “The best of all books !”);

cout << get<0>(t) << “;” << get<1>(t) << “;” << get<2>(t) << endl;

Concurrency : le multithreading !

Il est possible de lancer une tâche en parallèle et d’en attendre la fin. On va utiliser la classe thread disponible dans le fichier d’entête <thread>. Il suffit de passer une routine en argument du constructeur de la classe thread. La méthode join() sur l’objet thread permet d’en attendre la fin.

void ThreadFunc()

{

// ../..

}

void TestThread()

{

thread t(ThreadFunc);

std:thread::id id = t.get_id();

t.join();

cout << “TestThread: TID:” << id << “, ” << “Main:” << GetCurrentThreadId() << endl;

}

Dans l’exemple ci-dessus, la fonction du thread ne prend pas de paramètres. Cependant, dans certains cas, on veut pouvoir passer des paramètres au thread. Il suffit de passer les paramètres au constructeur de l’objet thread :

class Param

{

public:

string s;

int i;

float f;

};

void ThreadFunc2(Param param)

{

cout << param.s << “, ” << param.i << “, ” << param.f << endl;

// ../..

}

void TestThreadWithParam()

{

Param param = { “C++”, 14, 50.0 };

thread t(ThreadFunc2, param);

t.join();

}

Il existe des classes de type verrou comme mutex ou unique_lock<T> pour protéger l’accès aux données partagées. Vous pouvez remarquer que la gestion des threads est plutôt simple à utiliser.

Conclusion

Nous avons balayé les différentes composantes de la librairie standard et le constat est le suivant : le code source de la STL est complexe car c’est un framework extensible ; on l’a vu sur iostream et le passage d’une structure à cout ; qu’il faut apprendre à maîtriser. A partir des exemples simples présents dans cet article, vous n’avez plus qu’à vous lancer. Il y a des domaines que je n’ai pas couvert et c’est volontaire mais sachez que cet article couvre 90% de la STL. L’aspect le plus important est la maîtrise des containers. Ne construisez plus vos propres structures de list, hashtable ou autres, utilisez la STL ! La seule utilisation de vector<T> vous fait rentrer de plein pied dans la STL. Vous aurez des performances inégalables. N’utilisez plus les delete et passez aux smart pointers ! unique_ptr<T> et shared_ptr<T> justifient ; comme vector<T> ; une utilisation systématique. La fin des memory leak (fuite mémoire) en est une autre justification. Un conseil : laissez le code un peu ancien (legacy) avec un style à la papa, et sur le nouveau code, déchainez les enfers avec de la STL partout… J Tout ça est à prendre avec beaucoup de modération et de sagesse mais je suis certain que vous me comprenez. Le nouveau code doit être codé avec tous les artifices du C++ 11. Le code est plus lisible, les performances sont au rendez-vous. Vous n’avez plus aucunes excuses pour ne pas essayer ; à vous de jouer. C’est « power & performance » comme disait Herb Sutter, chairman du standard C++ ISO.