<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>blog.freelan.org &#187; &#187; cast</title>
	<atom:link href="https://blog.freelan.org/tag/cast/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.freelan.org</link>
	<description>De l&#039;informatique, des octets et des poneys.</description>
	<lastBuildDate>Fri, 04 Apr 2014 17:34:59 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>L&#8217;héritage en C++</title>
		<link>https://blog.freelan.org/2011/03/08/lheritage-en-c/</link>
		<comments>https://blog.freelan.org/2011/03/08/lheritage-en-c/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 17:40:01 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[cast]]></category>
		<category><![CDATA[conversion]]></category>
		<category><![CDATA[héritage]]></category>
		<category><![CDATA[inheritance]]></category>
		<category><![CDATA[privé]]></category>
		<category><![CDATA[protégé]]></category>
		<category><![CDATA[public]]></category>
		<category><![CDATA[virtuel]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=327</guid>
		<description><![CDATA[Tous les langages modernes offrent aujourd'hui, par leur nature orientée objet, un moyen puissant de réutiliser du code existant : l'héritage. La plupart des programmeurs sont bien entendu déjà familiers avec le principe de dériver d'une classe pour en étendre les possibilités (ou les restreindre) dans une classe fille.
Cependant, dans ce domaine, C++ va un peu plus loin que les autres langages en proposant différentes notions d'héritage public, privé, protégé et virtuel. Nous allons tenter, dans cet article, d'expliquer en détails leurs rôles et en quoi elles peuvent se rendre utiles.]]></description>
				<content:encoded><![CDATA[<p>Tous les langages modernes offrent aujourd&#8217;hui, par leur nature orientée objet, un moyen puissant de concevoir des hiérarchies : l&#8217;héritage. La plupart des programmeurs sont bien entendu déjà familiers avec le principe de dériver d&#8217;une classe pour en étendre les possibilités (ou les restreindre) dans une classe fille. Cependant, dans ce domaine, C++ va un peu plus loin que les autres langages en proposant différentes notions d&#8217;héritage public, privé, protégé et virtuel. Nous allons tenter, dans cet article, d&#8217;expliquer en détails leurs rôles et en quoi elles peuvent se rendre utiles.</p>
<h1>Un cas classique</h1>
<p></p><pre class="crayon-plain-tag">class Base
{
  public:
    int x() const { return m_x; }
  protected:
    void set_x(int value) { m_x = value; }
  private:
    int m_x;
};

class Derived : public Base
{
  public:
    void foo() { set_x(5); }
};</pre><p>Ce code n&#8217;évoque probablement rien de nouveau pour la plupart d&#8217;entre vous : nous déclarons une classe <code>Base</code>, qui possède une méthode <em>publique</em>, une méthode <em>protégée</em> et une variable membre <em>privée</em>. Nous déclarons également une classe <code>Derived</code>, qui <em>dérive</em> de <code>Base</code> au travers d&#8217;un <strong>héritage public</strong>. Ce principe d&#8217;héritage public est généralement simple à comprendre et il est présent dans pratiquement tous les langages objets : lorsque <code>Derived</code> dérive de <code>Base</code> au travers d&#8217;un <em>héritage public</em>, on dit aussi que <code>Derived</code> &#8220;<em>est un&#8221;</em> <code>Base</code>. Toute instance de <code>Derived</code> peut être considérée comme une instance de <code>Base</code>. Comme nous l&#8217;avions vu, <a href="http://blog.freelan.org/2011/01/17/les-casts-en-cpp/">dans un article précédent</a>, il est également possible de tester si une instance de <code>Base</code> est une instance de <code>Derived</code> grâce à un <code>dynamic_cast<></code>. De façon résumée, un héritage publique offre les garanties suivantes :</p>
<ul>
<li>Toutes les membres (variables et méthodes) <strong>publics</strong> dans la classe de base le sont aussi dans la classe dérivée.</li>
<li>Toutes les membres <strong>protégés</strong> dans la classe de base le sont aussi dans la classe dérivée.</li>
<li>Les membres <strong>privés</strong> de la classe de base ne sont pas accessibles à la classe dérivée.</li>
<li>On peut convertir <em>implicitement</em> un pointeur (respectivement une référence) sur la classe dérivée vers un pointeur (respectivement une référence) sur la classe de base.</li>
<li>On peut convertir <em>explicitement</em> un pointeur (respectivement une référence) sur la classe de base vers un pointeur (respectivement une référence) sur la classe dérivée.</li>
</ul>
<p>Ainsi dans notre exemple, <code>Derived</code> peut, dans sa méthode <code>foo()</code> (et <strong>indépendamment de la visibilité de cette méthode</strong>), appeler la méthode protégée de son parent, <code>set_x()</code>. Elle ne peut pas en revanche, directement accéder à <code>m_x</code> qui est dans une <strong>section privée</strong> de la classe de base.</p>
<h2>Le rôle de l&#8217;héritage public</h2>
<p>Une concept erroné va généralement de pair avec l&#8217;utilisation de l&#8217;héritage public : il consiste à dire que ce type d&#8217;héritage est utilisé principalement pour favoriser la <strong>réutilisation de code</strong>. Ceci n&#8217;est pas (ou ne devrait pas) être la raison : en C++, il existe beaucoup de moyens de réutiliser son code, la façon la plus efficace étant la <strong>composition</strong>. Le fait de d&#8217;utiliser un héritage public <em>doit</em> être une <strong>décision fonctionnelle</strong>, et pas une <strong>décision technique</strong> : ainsi, la classe <code>Table</code> dérive de la classe <code>Meuble</code> parce qu&#8217;une table <em>&#8220;est un&#8221;</em> meuble, et pas parce qu&#8217;en faisant ainsi on évite de retaper du code. L&#8217;héritage (public ou non) s&#8217;il apporte quelque-chose, c&#8217;est principalement de la <strong>flexibilité</strong>.</p>
<h2>Bonnes pratiques</h2>
<p>Certains auront peut être pensé la chose suivante : &#8220;La méthode <code>set_x()</code> ne sert à rien. Autant déclarer <code>m_x</code> <em>protégé</em> directement : il y aura moins de code&#8221;. Si cette remarque part surement d&#8217;une bonne intention (après tout, avoir moins de code est en général une très bonne idée), elle risque ici d&#8217;avoir des conséquences fâcheuses : En procédant de la même façon que dans l&#8217;exemple, nous ajoutons certes une méthode, mais nous réduisons également le <strong>couplage</strong>. En optant pour la solution &#8220;simplifiée&#8221;, si pour une raison ou pour un autre, je suis amené à modifier l&#8217;implémentation de <code>Base</code> et à renommer par exemple <code>m_x</code> en <code>m_first_x</code>, je devrais alors modifier non seulement tout le code de <code>Base</code> mais aussi tout le code de <code>Derived</code> (puisque nous y faisons référence à <code>m_x</code> directement). Le fait d&#8217;ajouter une méthode protégée à <code>Base</code> permet de figer son interface &#8220;publique&#8221; et réduit donc drastiquement le couplage. Il en résulte un code bien plus maintenable. En règle générale, on retiendra que les variables au sein d&#8217;une classe seront toujours soit <strong>publiques</strong>, soit <strong>privées</strong>, mais très rarement <strong>protégées</strong>. Et si dans un cas particulier vous sentez avoir vraiment besoin d&#8217;un accès à un membre privé, préférez la directive <code>friend</code> qui augmentera bien moins le couplage.</p>
<p>Cette règle n&#8217;est évidemment pas absolue et vous risquez de rencontrer un scénario où déclarer une variable membre <strong>protégée</strong> est la bonne chose à faire. Cependant, ça ne devrait logiquement pas être votre premier réflexe.</p>
<h1>L&#8217;héritage privé : une alternative à la composition</h1>
<p>Qui n&#8217;a jamais écrit par erreur, au cours d&#8217;une soirée (nuit ?) un peu trop longue quelque-chose de ce genre :</p><pre class="crayon-plain-tag">class Derived : Base
{
};</pre><p>Il en résulte en général de longues minutes très agaçantes où l&#8217;on tente de comprendre pourquoi le compilateur refuse systématiquement d&#8217;utiliser les variables de la classe parente. La raison est plutôt simple : en l&#8217;absence d&#8217;un attribut de visibilité, l&#8217;héritage par défaut en C++ est <strong>privé</strong>. Le code précédent est donc équivalent à :</p><pre class="crayon-plain-tag">class Derived : private Base
{
};</pre><p>Voilà une bonne occasion d&#8217;expliquer à quoi sert ce type d&#8217;héritage. L&#8217;héritage privé offre les garanties suivantes :</p>
<ul>
<li>Tous les membres <strong>publics</strong> dans la classe de base sont <strong>privés</strong> dans la classe dérivée.</li>
<li>Tous les membres <strong>protégés</strong> dans la classe de base sont <strong>privés</strong> dans la classe dérivée.</li>
<li>Les membres <strong>privés</strong> de la classe de base ne sont pas accessibles à la classe dérivée.</li>
<li>La classe dérivée peut redéfinir les méthodes <strong>virtuelles</strong> de la classe de base.</li>
<li>Toutes les méthodes de la classe dérivée peuvent convertir un pointeur (respectivement une référence) sur <code>Derived</code> en pointeur (respectivement une référence) sur <code>Base</code>. <strong>Ceci n&#8217;est en revanche normalement pas possible en dehors de la classe dérivée.</strong> (Voir tout de même la remarque).</li>
</ul>
<p>Là où un héritage <em>public</em> traduit une relation de type &#8220;est un&#8221;, un héritage <em>privé</em> lui traduit une relation de type &#8220;est implémenté en tant que&#8221;. En pratique, un héritage privé est comparable à une composition de type 1-vers-1. Prenons un exemple plus parlant :</p><pre class="crayon-plain-tag">class Engine
{
  public:
    void start();
};

class Car : private Engine
{
  public:
    using Engine::start;
};</pre><p>Nous avons déclaré une classe <code>Engine</code> (moteur) et <code>Car</code> (voiture) qui en dérive de façon privée. Dans la déclaration de <code>Car</code>, nous indiquons, au sein d&#8217;une section publique et grâce au mot clé <code>using</code>, que nous <em>utilisons</em> la méthode <code>start()</code> de la classe parente. Celle-ci devient donc publique pour la classe fille et en dehors. Dans ce cas, on voit bien qu&#8217;un héritage public n&#8217;aurait aucun sens puisqu&#8217;une voiture &#8220;n&#8217;est pas&#8221; un moteur, elle &#8220;possède un moteur&#8221;. Certains se demandent peut être si une composition n&#8217;est pas plus indiquée dans ce cas et la réponse n&#8217;est pas évidente :</p>
<ul>
<li>En règle générale, oui, préférez la composition à l&#8217;héritage&#8230;</li>
<li>&#8230; mais dans le cas que nous présentons ici, la composition est assez &#8220;forte&#8221; : une voiture ne peut toujours avoir <strong>qu&#8217;un seul</strong> moteur, et le fait de démarrer la voiture <em>revient à</em> démarrer le moteur.</li>
</ul>
<p>Les cas où l&#8217;on peut légitimement accepter un héritage privé en lieu et place d&#8217;une composition restent très limités. Et personne ne vous blâmera si même dans ce cas, vous optez pour la composition. L&#8217;héritage privé est surtout une facilité du langage pour éviter au programmeur de saisir du code inutile : dans notre exemple, en utilisant une composition, on aurait obtenu quelque-chose de ce genre :</p><pre class="crayon-plain-tag">class Engine
{
  public:
    void start();
};

class Car
{
  public:
    void start() { m_engine.start(); }
  private:
    Engine m_engine;
};</pre><p>Pas vraiment plus difficile à comprendre, mais un peu plus long à taper. Et si ce n&#8217;était pas une mais vingt méthodes de <code>Engine</code> qu&#8217;il avait fallu &#8220;transporter&#8221; dans l&#8217;interface publique de <code>Car</code>, je vous laisse imaginer le temps perdu à saisir toutes les méthodes triviales qui ne font qu&#8217;appeler la &#8220;vraie&#8221; méthode du sous-objet. Dans tous les cas, notez qu&#8217;en t&#8217;en qu&#8217;utilisateur de la classe <code>Car</code>, vous ne <strong>devez jamais</strong> prendre en compte la présence d&#8217;un héritage privé dans sa définition : cet héritage est privé et <strong>relève de l&#8217;implémentation</strong> : l&#8217;auteur de la classe peut à tout moment décider de réécrire sa classe pour utiliser une composition classique, ou encore de redéfinir à la main chacune des méthodes, sans vous en notifier !</p>
<h2>Remarque</h2>
<p>Un lecteur assidu aura peut être remarqué une contradiction avec <a href="http://blog.freelan.org/2011/01/17/les-casts-en-cpp/">un de mes articles précédents</a> : en effet, si j&#8217;ai indiqué un peu plus haut qu&#8217;il n&#8217;était pas possible de convertir un <code>Car*</code> en <code>Engine*</code> en dehors des méthodes de <code>Car</code>, ce n&#8217;était pas tout à fait vrai : cela est possible en C++, par l’intermédiaire d&#8217;une &#8220;conversion à la façon C&#8221;. Évidemment, si on respecte la clause énoncée précédemment de ne <strong>jamais</strong> se baser sur le détail d&#8217;implémentation que représente l&#8217;<em>héritage privé</em>, il ne s&#8217;agit en fait que d&#8217;un argument supplémentaire contre l&#8217;utilisation de ce type de conversion. En un mot : faites-le une fois pour vous amuser, puis plus jamais !</p>
<h2>À propos du mot clé using</h2>
<p>S&#8217;il est très courant de rencontrer le mot clé <code>using</code> lors de l&#8217;utilisation d&#8217;héritage privé, il est également possible de l&#8217;utiliser lors d&#8217;un héritage &#8220;classique&#8221; public :</p><pre class="crayon-plain-tag">class Base
{
  public:
    int x() const { return m_x; }
  protected:
    void set_x(int value) { m_x = value; }
  private:
    int m_x;
};

class Derived : public Base
{
  public:
    using Base::set_x;
};</pre><p>Ici, nous avons <strong>augmenté la visibilité</strong> de la méthode <code>set_x()</code> en la rendant <strong>publique</strong> dans la classe dérivée, alors qu&#8217;elle n&#8217;était que <strong>protégée</strong> dans la classe de base. Bien que peu courante, cette syntaxe n&#8217;en demeure pas moins tout à fait correcte.</p>
<h1>L&#8217;héritage protégé</h1>
<p>L&#8217;héritage <strong>protégé</strong> ressemble énormément à l&#8217;héritage <strong>privé</strong>. En fait, sa seule différence est la suivante :</p>
<ul>
<li>Tout membre <strong>public</strong> ou <strong>protégé</strong> hérité au travers d&#8217;un <strong>héritage protégé</strong> est également accessible de façon <strong>protégée</strong> aux <strong>classes filles</strong> de la classe dérivée.</li>
</ul>
<p>Ce qui se traduit par :</p><pre class="crayon-plain-tag">class Engine
{
  protected:
    void start() {};
};

class Car : protected Engine {};

class Robot : protected Car
{
  public:
    using Engine::start;
};</pre><p>Ici <code>Robot</code> peut accéder à la méthode protégée de <code>Engine</code> parce que <code>Car</code> hérite de <code>Engine</code> au travers d&#8217;un <strong>héritage protégé</strong>. On voit que si l&#8217;héritage privé peut s&#8217;avérer utile dans certains cas, l&#8217;héritage protégé lui a un intérêt beaucoup plus limité, car le fait de pouvoir accéder aux méthodes de sa classe <em>grand-parente</em> ne fait qu&#8217;<strong>augmenter le couplage</strong>.</p>
<p>Je n&#8217;ai encore jamais rencontré ou eu besoin d&#8217;un héritage protégé depuis que je développe en C++.</p>
<h1>L&#8217;héritage virtuel</h1>
<p>Pour expliquer en quoi consiste l&#8217;héritage virtuel, replaçons avant tout dans le contexte quelques lieux communs :</p>
<h2>L&#8217;héritage multiple</h2>
<p>C++ est l&#8217;un des rares langages à autoriser l&#8217;héritage multiple là où les autres langages préfèrent imposer la notion d&#8217;interfaces. La légitimité de l&#8217;une ou de l&#8217;autre de ces techniques est un sujet à part entière et sort du cadre de cet article. Si vous vous intéressez à ce débat, il existe sur l&#8217;Internet <a href="http://www.artima.com/intv/abcs.html">bien des discussions</a> à ce sujet.</p>
<h3>La minute &#8220;rebelle&#8221;</h3>
<p>Vous avez certainement déjà entendu quelqu&#8217;un dire une chose du genre : &#8220;L&#8217;héritage multiple c&#8217;est toujours une hérésie, ça n&#8217;aurait jamais du exister ! Je n&#8217;ai jamais réussi à en faire quoi que ce soit d&#8217;utile.&#8221;</p>
<p>Face à ce genre de remarque simpliste, j&#8217;ai tendence à avoir la même réaction que <a title="www.parashift.com/" href="http://www.parashift.com/" target="_blank">Marshall Cline</a> : les gens qui vous disent ça ne <strong>connaissent</strong> à priori <strong>pas votre problème</strong> ou vos besoins, et pourtant ils prétendent y répondre : comment le pourraient-ils ? Comment peuvent-ils savoir que dans <strong>votre situation</strong>, l&#8217;héritage multiple n&#8217;est pas la meilleure solution ? Si vous rencontrez ce genre de personnes, soyez prudents : le manque d&#8217;ouverture d&#8217;esprit et de réflexion fait en général de bien piètres programmeurs. Une phrase que j&#8217;aime bien citer dans cette situation est celle-ci : &#8220;Toutes les phrases qui dénoncent un principe de façon absolue et générale sont absolument et généralement fausses&#8221;. Si l&#8217;héritage multiple semble être la solution à votre problème, n&#8217;hésitez pas à l&#8217;utiliser.</p>
<p>Dans tous les cas, réfléchissez toujours bien à ce que vous faites : si l&#8217;héritage multiple est tant victime d&#8217;à priori négatifs, c&#8217;est qu&#8217;il est parfois difficile de comprendre son utilisation.</p>
<h2>La démonstration par l&#8217;exemple</h2>
<p>Pour illustrer les notions d&#8217;héritage multiple et virtuel, créons tout d&#8217;abord une hiérarchie de classes :</p>
<div id="attachment_341" style="width: 434px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/03/shapes.png"><img class="size-full wp-image-341" title="Hiérarchie de classe à héritage multiple" src="http://blog.freelan.org/wp-content/uploads/2011/03/shapes.png" alt="" width="424" height="365" /></a><p class="wp-caption-text">Hiérarchie de classe à héritage multiple</p></div>
<p>Comme nous le voyons ici, nous partons d&#8217;une classe <code>Shape</code> (&#8220;forme&#8221; en anglais), de laquelle dérivent deux classes <code>Rectangle</code> et <code>Diamond</code> (&#8220;losange&#8221; en anglais). Une quatrième classe, <code>Square</code> (&#8220;carré&#8221; en anglais) hérite à la fois de <code>Rectangle</code> et de <code>Diamond</code>.</p>
<p>Une implémentation naïve de cette hiérarchie en C++ pourrait ressembler à :</p><pre class="crayon-plain-tag">class Shape
{
  public:
    int identifier() const { return m_identifier; }
    virtual void draw() = 0;
  private:
    int m_identifier;
};

class Rectangle : public Shape
{
  public:
    void draw() { /* ... */ }
};

class Diamond : public Shape
{
  public:
    void draw() { /* ... */ }
};

class Square : public Rectangle, public Diamond
{
  public:
    void draw() { /* ... */ }
};</pre><p>Cela a du sens : un carré (si on s&#8217;en réfère aux lois de la géométrie) est à la fois un rectangle et un losange, et il est aussi une forme.</p>
<p>Cependant, ce code n&#8217;a en pratique pas la structure souhaitée :</p>
<p>En effet, en utilisant un héritage classique (non virtuel) <code>Square</code> hérite en pratique deux fois de <code>Shape</code> : une fois par la branche de gauche (<code>Rectangle</code>), et une fois par la branche de droite (<code>Diamond</code>). Il en résulte que chaque instance de <code>Square</code> possède deux instances de <code>m_identifier</code>. Lorsqu&#8217;on souhaite utiliser <code>identifier()</code> ou <code>m_identifier</code> dans ou en dehors de la classe <code>Square</code>, il faut préciser par quelle branche on passe :</p>
<ul>
<li>Soit en préfixant m_identifier par <code>Diamond</code> ou <code>Rectangle</code> (&#8220;<code>Rectangle::m_identifier</code>&#8220;);</li>
<li>soit en effectuant auparavant une conversion de <code>this</code> vers <code>Rectangle*</code> ou <code>Diamond*</code> (&#8220;<code>static_cast<Rectangle*>(this)->m_identifier</code>&#8220;)</li>
</ul>
<p>Ce n&#8217;est généralement pas ce qui est souhaité.</p>
<p>Pour résoudre ce problème, nous avons besoin de l&#8217;<strong>héritage virtuel</strong> :</p><pre class="crayon-plain-tag">class Shape
{
  public:
    int identifier() const { return m_identifier; }
    virtual void draw() = 0;
  private:
    int m_identifier;
};

class Rectangle : public virtual Shape // Notez la pr&eacute;sence de &quot;virtual&quot; ici
{
  public:
    void draw() { /* ... */ }
};

class Diamond : public virtual Shape // Notez la pr&eacute;sence de &quot;virtual&quot; ici
{
  public:
    void draw() { /* ... */ }
};

class Square : public Rectangle, public Diamond
{
  public:
    void draw() { /* ... */ }
};</pre><p>En spécifiant que <code>Rectangle</code> et <code>Diamond</code> héritent tous deux virtuellement de <code>Shape</code>, nous empêchons la <em>multiple instanciation</em> du type de base : il n&#8217;y a alors plus qu&#8217;une seule instance de <code>m_identifier</code> et on peut y référer directement sans avoir recours à des conversions.</p>
<h2>Bonnes pratiques</h2>
<p>L&#8217;utilisation de l&#8217;héritage virtuel peut se faire conjointement avec tout autre type d&#8217;héritage (privé, protégé et public), mais est habituellement rencontré principalement avec l&#8217;<strong>héritage public</strong>.</p>
<p>Indépendamment de l&#8217;utilisation de l&#8217;héritage virtuel, on constate assez logiquement que les classes les plus en haut de la hiérarchie devraient dans l&#8217;idéal être virtuelles pures. Si c&#8217;est le cas, cela ne signifie <strong>pas forcément</strong> qu&#8217;il faille supprimer l&#8217;héritage virtuel : outre le fait de ne pas dupliquer inutilement les instances des membres, l&#8217;héritage virtuel permet également de s&#8217;assurer que la classe de base n&#8217;a qu&#8217;<strong>une seule adresse</strong> dans les instances filles. Il est en pratique très probable que vous deviez user d&#8217;héritage virtuel lorsque vous utilisez l&#8217;héritage multiple.</p>
<h1>Conclusion</h1>
<p>Nous avons vu au travers des différentes sections que C++ est très complet en matière d&#8217;héritage. Élément incontournable de la programmation orientée objet moderne, c&#8217;est un principe qu&#8217;il convient de manier avec la plus grande précaution et une bonne réflexion : si l&#8217;héritage peut résoudre bien des problèmes, il n&#8217;est de loin pas la solution universelle. Que vous optiez pour l&#8217;héritage, la composition ou autre chose pour la résolution de vos problèmes, réfléchissez toujours et <strong>envisagez chacune des possibilités</strong>.</p>
<p>Comme toujours, n&#8217;hésitez pas à me faire part de vos remarques, questions ou corrections dans les commentaires.</p>
<h1>Sources</h1>
<p>Voici une série de liens en anglais qui m&#8217;ont inspiré pour la rédaction de cet article. N&#8217;hésitez pas à les consulter, ils sont extrêmement intéressants :</p>
<ul>
<li><a href="http://www.parashift.com/c++-faq-lite/private-inheritance.html">L&#8217;héritage privé/protégé sur parashift</a></li>
<li><a href="http://stackoverflow.com/questions/4605556/when-virtual-inheritance-is-a-good-design">StackOverflow</a></li>
<li><a href="http://en.allexperts.com/q/C-1040/virtual-inheritance.htm">L&#8217;héritage virtuel sur allexperts</a></li>
<li><a href="http://www.parashift.com/c++-faq-lite/multiple-inheritance.html">L&#8217;héritage virtuel sur parashift</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/03/08/lheritage-en-c/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Les casts en C++</title>
		<link>https://blog.freelan.org/2011/01/17/les-casts-en-cpp/</link>
		<comments>https://blog.freelan.org/2011/01/17/les-casts-en-cpp/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 17:30:05 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[cast]]></category>
		<category><![CDATA[const_cast]]></category>
		<category><![CDATA[conversion]]></category>
		<category><![CDATA[conversions]]></category>
		<category><![CDATA[dynamic_cast]]></category>
		<category><![CDATA[reinterpret_cast]]></category>
		<category><![CDATA[static_cast]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=196</guid>
		<description><![CDATA[Nombreux sont les programmeurs C++ qui ont d'abord été confrontés au C. Les deux langages partagent en effet bien des fonctionnalités... mais ont également de grandes différences.

Parmi ces différences, on trouve les opérateurs de conversion C++. Ils sont certainement l'un des points les plus mal compris par les développeurs C qui voient souvent en eux un verbiage inutile. L'objectif de cet article est de (dé)montrer l'utilité des opérateurs de conversion C++, en comparaison avec les conversions classiques, dites : "à la C" et de comprendre ce qu'ils peuvent apporter au programmeur en termes de maintenabilité et de sécurité.]]></description>
				<content:encoded><![CDATA[<p>Nombreux sont les programmeurs C++ qui ont d&#8217;abord été confrontés au C. Les deux langages partagent en effet bien des fonctionnalités&#8230; mais ont également de grandes différences.</p>
<p>Parmi ces différences, on trouve les opérateurs de conversion C++. Ils sont certainement l&#8217;un des points les plus mal compris par les développeurs C qui voient souvent en eux un verbiage inutile. L&#8217;objectif de cet article est de (dé)montrer l&#8217;utilité des opérateurs de conversion C++, en comparaison avec les conversions classiques, dites : &#8220;à la C&#8221; et de comprendre ce qu&#8217;ils peuvent apporter au programmeur en termes de maintenabilité et de sécurité.</p>
<h1>Un petit mot sur les conversions</h1>
<p>Les conversions (ou &#8220;cast&#8221; en anglais) sont un des outils incontournables du programmeur C++. Mais comme tout outil, il faut savoir les utiliser à bon escient.</p>
<p>Dans l&#8217;idéal, un programme doit contenir le moins possible de &#8220;casts&#8221; : les types doivent s&#8217;interfacer naturellement les uns avec les autres. Cela garantit un <strong>découplage</strong> du code et donc une meilleure maintenabilité. Cela ne signifie pas qu&#8217;il faille à tout prix éviter les &#8220;casts&#8221; mais simplement qu&#8217;il faut les utiliser avec parcimonie.</p>
<p>Dans les sections qui suivent, nous allons expliquer le rôle de chaque opérateur de conversion. Pour l&#8217;ensemble des sections, nous considérerons les classes suivantes lorsqu&#8217;il sera question de hiérarchie :</p><pre class="crayon-plain-tag">class Base { public: virtual ~Base() {} };
class Derived : public Base {};
class Derived2 : public Base {};</pre><p></p>
<h1>static_cast&lt;&gt;</h1>
<p>Il permet plusieurs choses :</p>
<ul>
<li>Expliciter les conversions implicites, supprimant du même fait <strong>tout avertissement</strong> que donnerait le compilateur si la conversion peut entraîner un risque. Exemple : <strong>double</strong> vers <strong>int</strong>.</li>
<li>Convertir vers et depuis n&#8217;importe quel type pointé à partir d&#8217;un <strong>void*</strong>. Exemple : <strong>void*</strong> vers <strong>unsigned char*</strong>.</li>
<li>Convertir au travers d&#8217;une hiérarchie de classe, <strong>sans effectuer de vérification préalable</strong>. Exemple : <strong>Base*</strong> vers <strong>Derived*</strong> ou <strong>Base&amp;</strong> vers <strong>Derived&amp;</strong>.</li>
<li>Ajouter l&#8217;attribut constant au type converti. Exemple : <strong>char*</strong> vers <strong>const char*</strong>.</li>
</ul>
<p>Dans le dernier cas, notez que puisqu&#8217;il n&#8217;y a <strong>aucune vérification</strong> et que <strong>static_cast&lt;&gt;</strong> n&#8217;échoue <strong>jamais</strong>, le code suivant a un <strong>comportement indéfini</strong> (communément nommé en anglais &#8220;<em>undefined behavior</em>&#8221; ou &#8220;<em>UB</em>&#8220;) :</p><pre class="crayon-plain-tag">void foo()
{
Base* base = new Derived();

// Ce code est valide et correct car base pointe en fait vers une instance de Derived.
Derived* derived = static_cast&amp;lt;Derived*&amp;gt;(base);

// Ce code compile mais &agrave; un comportement ind&eacute;fini (Undefined Behavior)
// car base ne pointe pas vers une instance de Derived2.
Derived2* derived2 = static_cast&amp;lt;Derived2*&amp;gt;(base);
}</pre><p>Notez que la notion de <strong>comportement indéfini</strong> n&#8217;offre par définition <strong>aucune garantie</strong> : le code peut avoir le comportement espéré, faire crasher le programme ou provoquer l&#8217;envoi d&#8217;un missile nucléaire sur Cuba.</p>
<p>Il ne permet <strong>pas</strong> de :</p>
<ul>
<li>Convertir vers ou depuis un type pointé à partir d&#8217;un autre type pointé <strong>autre que void*</strong>. Exemple : <strong>unsigned char*</strong> vers <strong>char*</strong>.</li>
<li>Tester qu&#8217;une instance est celle d&#8217;un type dérivé. Exemple : tester qu&#8217;un <strong>Base*</strong> est en fait un <strong>Derived*</strong>.</li>
<li>Supprimer l&#8217;attribut <strong>constant</strong> du type converti. Exemple : <strong>const char*</strong> vers <strong>char*</strong>.</li>
</ul>
<h2>En bref</h2>
<p><strong>static_cast&lt;&gt;</strong> est sans doute l&#8217;opérateur de conversion que vous serez amené à utiliser le plus. Il ne permet que de réaliser des conversions sûres et à pour rôle principal celui d&#8217;expliciter les conversions implicites.</p>
<p>Dans le cas du polymorphisme, il est à préférer à <strong>dynamic_cast&lt;&gt;</strong> lorsque l&#8217;on a <strong>la garantie</strong> que la conversion va réussir.</p>
<h1>dynamic_cast&lt;&gt;</h1>
<p>Le seul rôle de <strong>dynamic_cast&lt;&gt;</strong> est de tester à l&#8217;exécution si un pointeur d&#8217;un type de base est en fait un pointeur vers un type dérivé.</p>
<p>Exemple :</p><pre class="crayon-plain-tag">void foo()
{
Base* base = new Derived();

// Apr&egrave;s l'appel, derived pointe vers l'instance de Derived
// car base pointe en fait vers une instance de Derived.
Derived* derived = dynamic_cast&amp;lt;Derived*&amp;gt;(base);

// Apr&egrave;s l'appel, derived2 vaut 0
// car base ne pointe pas vers une instance de Derived2.
Derived2* derived2 = dynamic_cast&amp;lt;Derived2*&amp;gt;(base);

// Dans le cas d'une r&eacute;f&eacute;rence, si le cast n'est pas possible
// Une exception de type std::bad_alloc est lanc&eacute;e.
Derived2&amp;amp; derived2_bis = dynamic_cast&amp;lt;Derived2&amp;amp;&amp;gt;(*base);
}</pre><p>Note : pour que <strong>dynamic_cast&lt;&gt;</strong> fonctionne, le type de base doit posséder <strong>au moins une</strong> méthode virtuelle.</p>
<p>Un appel à <strong>dynamic_cast&lt;&gt;</strong> est plus coûteux qu&#8217;un appel à <strong>static_cast&lt;&gt;</strong>car <strong>dynamic_cast&lt;&gt;</strong> effectue une recherche dans la &#8220;<em>v-table</em>&#8221; de l&#8217;instance <strong>à l&#8217;exécution</strong> pour déterminer son type exact.</p>
<p>On veillera donc à n&#8217;utiliser <strong>dynamic_cast&lt;&gt;</strong> que lorsqu&#8217;il n&#8217;y a aucune autre solution.</p>
<h2>En bref</h2>
<p><strong>dynamic_cast&lt;&gt;</strong> est le seul opérateur de conversion à avoir un effet &#8220;indéterminé&#8221; jusqu&#8217;à l&#8217;exécution. Son utilisation n&#8217;a de sens que lorsque confronté à du polymorphisme. Dans les cas où la conversion est assurée de réussir, on lui préfèrera <strong>static_cast&lt;&gt;</strong> plus rapide et ne nécessitant pas que les classes possèdent une méthode virtuelle.</p>
<h1>const_cast&lt;&gt;</h1>
<p><strong>const_cast&lt;&gt;</strong> permet de <strong>supprimer</strong> l&#8217;attribut <em>constant</em> ou <em>volatile</em> d&#8217;une <strong>référence</strong> ou d&#8217;un <strong>type pointé</strong>. Exemple : <strong>const char*</strong> vers <strong>char*</strong> ou <strong>volatile int</strong> vers <strong>int</strong>.</p>
<p>C&#8217;est notamment <strong>le seul</strong> opérateur de conversion à pouvoir le faire : même <strong>reinterpret_cast&lt;&gt;</strong> n&#8217;a pas ce pouvoir.</p><pre class="crayon-plain-tag">void foo()
{
char* buf = new char[16];

// L'ajout de l'attribut const ne n&eacute;cessite pas de cast
// Il est implicite
const char* cbuf = buf;

// Le retrait de l'attribut const n&eacute;cessite const_cast&amp;lt;&amp;gt;.
char* buf2 = const_cast&amp;lt;char*&amp;gt;(cbuf);
}</pre><p></p>
<h2>L&#8217;importance d&#8217;écrire un code &#8220;const-correct&#8221;</h2>
<p>Directement relié aux opérateurs de conversion, l&#8217;écriture d&#8217;un code const-correct est un autre aspect du C++ souvent mal perçu par les programmeurs C. Le C est plus ancien et le mot clé <strong>const</strong> n&#8217;y a pas toujours existé; il a été emprunté au C++ par la suite.</p>
<p>Le fait d&#8217;indiquer qu&#8217;une variable est constante est un outil puissant permettant au compilateur de nous signaler certaines de nos erreurs qui auraient autrement passé la barrière de la compilation.</p>
<p>Qui ne s&#8217;est jamais trompé dans l&#8217;ordre des arguments d&#8217;un <strong>memcpy()</strong> ?</p><pre class="crayon-plain-tag">void foo()
{
const char* source = &quot;blog.freelan.org&quot;;
char* destination = new char[16];

// Oups ! Je me suis tromp&eacute; dans l'ordre des arguments...
// ... heureusement, puisque memcpy sp&eacute;cifie que le premier param&egrave;tre est un void*
// et pas un const void*, le compilateur m'indique une erreur.
memcpy(source, destination, strnlen(source));
}</pre><p></p>
<h2>Les mots clé &#8220;const&#8221; ou &#8220;volatile&#8221; appliqués aux classes</h2>
<p>En C++, les mots clé <strong>const</strong> et <strong>volatile</strong> s&#8217;appliquent évidemment aussi aux instances de classes mais ont des sémantiques différentes :</p>
<p>Le caractère <strong>const</strong> ou <strong>volatile</strong> s&#8217;applique récursivement aux membres de l&#8217;instance.</p>
<p>Il n&#8217;est possible d&#8217;appeler une méthode d&#8217;une classe que dans les cas suivants :</p>
<ul>
<li>l&#8217;instance n&#8217;est pas <strong>const</strong>.</li>
<li>l&#8217;instance est <strong>const</strong> et la méthode est déclarée <strong>const</strong>.</li>
<li>l&#8217;instance est déclarée <strong>volatile</strong> et la méthode est déclarée <strong>volatile</strong>.</li>
<li>l&#8217;instance est déclarée <strong>const</strong> et <strong>volatile</strong> et la méthode est elle aussi déclarée <strong>const</strong> et <strong>volatile</strong>.</li>
</ul>
<h2>À propos de &#8220;volatile&#8221;</h2>
<p>Certains lecteurs peuvent être perdus à la lecture du mot clé <strong>volatile</strong> qui, il faut bien l&#8217;avouer, n&#8217;est pas utilisé très souvent. Décrire précisément le rôle de <strong>volatile</strong> mériterait un article bien à part mais je vais tout de même dire en deux mots à quoi il sert :</p>
<p>Lorsqu&#8217;une variable est déclarée <strong>volatile</strong>, le compilateur n&#8217;a pas le droit d&#8217;optimiser sa valeur (mise en cache processeur) lors de tests.</p>
<p>Ainsi sans <strong>volatile</strong> sur la variable <strong>do_loop</strong>, le code suivant :</p><pre class="crayon-plain-tag">void foo()
{
bool do_loop = true;
while (do_loop) { doSomething(); }
}</pre><p>Risquerait d&#8217;être optimisé en tant que :</p><pre class="crayon-plain-tag">void foo()
{
while (true) { doSomething(); }
}</pre><p>Ce qui est correct dans la plupart des cas&#8230; sauf si <strong>do_loop</strong> peut être modifié par un autre <strong>thread</strong>. C&#8217;est principalement dans ce genre de cas que <strong>volatile</strong> trouve son utilité.</p>
<h2>Erreurs courantes</h2>
<p>Une erreur courante concernant <strong>const_cast&lt;&gt;</strong> consiste à supposer que l&#8217;on peut <em>toujours</em> supprimer le caractère <strong>constant</strong> d&#8217;une variable.</p>
<p>Ceci est évidemment faux : on ne peut supprimer le caractère constant (respectivement volatile) d&#8217;une variable que lorsque celle-ci a été déclarée <strong>non-const</strong> (respectivement <strong>non-volatile</strong>).</p>
<p>Ainsi le code suivant a un comportement indéfini :</p><pre class="crayon-plain-tag">void foo()
{
const char* cbuf = &quot;blog.freelan.org&quot;;

// L'utilisation de const_cast&amp;lt;&amp;gt; ici a un comportement ind&eacute;fini (undefined behavior)
// car cbuf a &eacute;t&eacute; d&eacute;clar&eacute; const.
char* buf = const_cast&amp;lt;char*&amp;gt;(cbuf);
}</pre><p>Un autre cas courant est celui des variables membres qui servent à mettre en cache un résultat :</p><pre class="crayon-plain-tag">class MyClass
{
public:

MyClass() : m_value_cache(0) {}

int getValue() const
{
if (m_value_cache == 0)
const_cast&amp;lt;int&amp;amp;&amp;gt;(m_value_cache) = computeValue();

return m_value_cache;
}

private:

static int computeValue();

int m_value_cache;
};</pre><p>L&#8217;utilisation de <strong>const_cast&lt;&gt;</strong> ici est erronée : si on déclare une instance <strong>const</strong> de MyClass, m_value_cache est aussi <strong>const</strong> lors de sa définition. L&#8217;utilisation de <strong>const_cast&lt;&gt;</strong> est la même que dans l&#8217;exemple précédent et a <strong>comportement indéfini</strong>.</p>
<p>La bonne solution est d&#8217;utiliser le mot clé <strong>mutable</strong>, qui permet à une variable membre de ne pas avoir les mêmes contraintes <strong>const</strong>/<strong>volatile</strong> que son instance parente :</p><pre class="crayon-plain-tag">class MyClass
{
public:

MyClass() : m_value_cache(0) {}

int getValue() const
{
// m_value_cache est d&eacute;clar&eacute; mutable
// il peut donc changer, m&ecirc;me dans une m&eacute;thode const
if (m_value_cache == 0)
m_value_cache = computeValue();

return m_value_cache;
}

private:

static int computeValue();

// Utilisation du mot cl&eacute; &quot;mutable&quot;
mutable int m_value_cache;
};</pre><p></p>
<h2>En bref</h2>
<p><strong>const_cast&lt;&gt;</strong> est le seul opérateur de conversion à pouvoir supprimer le caractère <strong>const</strong> ou <strong>volatile</strong> d&#8217;une variable. L&#8217;utilisation de <strong>const_cast&lt;&gt;</strong> doit rester très rare : le contraire indique souvent une importante erreur de design. Son seul usage habituellement toléré est l&#8217;interfaçage avec des bibliothèques historiques qui ne sont pas <strong>const-correct</strong>.</p>
<h1>reinterpret_cast&lt;&gt;</h1>
<p>Il s&#8217;agit de l&#8217;opérateur de conversion le plus dangereux, et du plus mal utilisé. Son rôle est de dire au compilateur : &#8220;réinterprète-moi la représentation binaire de ce type en tant qu&#8217;un autre type&#8221;.</p>
<p>Il permet :</p>
<ul>
<li>De convertir <strong>n&#8217;importe quel</strong> type pointé en une autre, même lorsque ceux-ci n&#8217;ont <strong>aucun rapport</strong>. Exemple : <strong>int*</strong> vers <strong>double*</strong>.</li>
<li>De convertir un type pointé en sa <strong>représentation intégrale</strong> et vice et versa. Exemple : <strong>int*</strong> vers <strong>int</strong>.</li>
</ul>
<p>Il est à noter que ces conversions sont <strong>dépendantes de l&#8217;implémentation</strong>. En d&#8217;autres termes, le compilateur est libre de faire ce qu&#8217;il veut concernant la conversion basée sur <strong>reinterpret_cast&lt;&gt;</strong> mais ce comportement doit être constant : il ne s&#8217;agit <strong>pas</strong> de comportement indéfini; le comportement est bien défini, simplement pas par le standard C++ mais votre version du compilateur. Si vous vous basez sur cette dépendance de l&#8217;implémentation, votre code est donc <strong>non-portable</strong>.</p>
<p>La seule garantie délivrée par le standard C++ concernant <strong>reinterpret_cast&lt;&gt;</strong> est que si vous convertissez un type A en un type B, puis de nouveau en un type A, le comportement est bien défini et vous récupérez bien la valeur de départ.</p>
<p>On comprend dès lors facilement le danger que peut représenter <strong>reinterpret_cast&lt;&gt;</strong>.</p>
<p>Voici un exemple d&#8217;utilisation :</p><pre class="crayon-plain-tag">void foo()
{
int a = 5;

// Le contenu de b d&eacute;pend du compilateur utilis&eacute;
// et n'est pas d&eacute;fini par le standard
double* b = reinterpret_cast&amp;lt;double*&amp;gt;(&amp;amp;a);

// Ici, on a la garantie que *c vaudra a, c'est &agrave; dire 5.
int* c = reinterpret_cast&amp;lt;int*&amp;gt;(b);
}</pre><p></p>
<h2>Cas particuliers</h2>
<p>Le peu de garanties associées à <strong>reinterpret_cast&lt;&gt;</strong> rendent celui-ci quasiment inutile dans la plupart des cas. Il y a cependant certaines exceptions de fait qui justifient une utilisation de <strong>reinterpret_cast&lt;&gt;</strong> sans nuire à la portabilité :</p>
<p>Les conversions entre les types <strong>char*</strong> et <strong>unsigned char*</strong> bien que non spécifiées par le standard, sont en pratique supportées par tous les compilateurs et produisent le comportement attendu. Le compilateurs ont par ailleurs de plus fortes contraintes à leur égard (spécifiquement au niveau de leur représentation) pour des raisons de compatibilité ascendante avec le C.</p>
<p>Vous pouvez donc clairement supposer qu&#8217;un <strong>reinterpret_cast&lt;&gt;</strong> entre un <strong>char*</strong> et un <strong>unsigned char*</strong> sera à la fois portable et défini.</p>
<h2>Polymorphisme</h2>
<p><strong>reinterpret_cast&lt;&gt;</strong> utilisé dans le cadre d&#8217;une conversion faisant intervenir du polymorphisme a un <strong>comportement non défini</strong>. Il n&#8217;est ainsi pas correct d&#8217;effectuer un <strong>reinterpret_cast&lt;&gt;</strong> entre par exemple un <strong>Base*</strong> et un <strong>Derived*</strong>.</p>
<h2>En bref</h2>
<p><strong>reinterpret_cast&lt;&gt;</strong> est l&#8217;opérateur de conversion le plus dangereux : permettant de faire ce qu&#8217;aucun autre ne peut faire (des conversions entres des types non liés) il convient de l&#8217;utiliser avec la plus grande prudence. En pratique, on lui préfèrera <strong>static_cast&lt;&gt;</strong> qui permet d&#8217;effectuer des conversions <strong>plus sûres</strong>, y compris vers et depuis des types pointés génériques (<strong>void*</strong>). Son seul usage toléré est l&#8217;interfaçage avec du code C ancien qui utilise pour ses paramètres de &#8220;buffer&#8221; des <strong>char*</strong> ou <strong>unsigned char*</strong> au lieu des <strong>void*</strong>.</p>
<h1>Old-school : les conversions &#8220;à la C&#8221;</h1>
<p>Le C++ supporte toujours l&#8217;ancienne syntaxe concernant les conversions &#8220;à la façon C&#8221;. Cependant, le standard précise clairement l&#8217;effet d&#8217;une telle conversion :</p>
<p>Le &#8220;cast&#8221; suivant : <strong>(Type)valeur</strong> ou <strong>Type(valeur)</strong></p>
<p>Sera équivalent à, par ordre de préférence :</p>
<ol>
<li>un <strong>const_cast&lt;&gt;</strong></li>
<li>un <strong>static_cast&lt;&gt;</strong></li>
<li>un <strong>static_cast&lt;&gt;</strong> suivi d&#8217;un <strong>const_cast&lt;&gt;</strong></li>
<li>un <strong>reinterpret_cast&lt;&gt;</strong></li>
<li>un <strong>reinterpret_cast&lt;&gt;</strong> suivi d&#8217;un <strong>const_cast&lt;&gt;</strong></li>
</ol>
<p>Les bonnes pratiques indiquent souvent que l&#8217;utilisation de ce type de conversion est à bannir, principalement parce qu&#8217;il peut résulter <em>silencieusement</em> en un <strong>reinterpret_cast&lt;&gt;</strong>, qui comme nous l&#8217;avons vu, peut se révéler <strong>extrêmement dangereux</strong>. De plus, l&#8217;usage des alternatives modernes aux opérateurs de conversion permet de <em>spécifier clairement l&#8217;intention du programmeur</em> et de <em>protéger contre les erreurs involontaires</em> (comme celles que nous avons vu avec <strong>const_cast&lt;&gt;</strong>).</p>
<h2>Une autre utilité</h2>
<p>Les &#8220;casts&#8221; à la C offrent également une possibilité qui n&#8217;est permise par <strong>aucun autre</strong> opérateur de conversion : celle de convertir vers une classe de base au travers d&#8217;un <strong>héritage privé</strong>. Ce type d&#8217;héritage est très souvent critiqué et fortement déconseillé. Je ne détaillerai pas ici les conséquences et les raisons de ce type d&#8217;héritage; c&#8217;est un sujet qui mérite son propre article.</p>
<h1>Conclusion</h1>
<p>Il y a beaucoup à dire sur les opérateurs de conversion et encore plus à apprendre. Nous avons vu que bien utilisés, ils sont un outil puissant et un allié du programmeur. Protégeant contre les erreurs involontaires et révélant les erreurs de conception, ils restent pour certains dangereux et sont tout de même à utiliser avec la plus grande précaution.</p>
<p>Une bonne connaissance de ces opérateurs de conversion et de leurs limites reste indispensable à la réalisation de programmes maintenables en C++.</p>
<h1>Références</h1>
<p>Voici une série de liens (en anglais, pour la plupart) qui m&#8217;ont inspiré dans la rédaction de cet article.</p>
<ul>
<li><a href="http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast">http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast</a></li>
<li><a href="http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-and-reinterpret-cast-be-used">http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-and-reinterpret-cast-be-used</a></li>
<li><a href="http://stackoverflow.com/questions/4708444/is-there-a-good-way-to-convert-from-unsigned-char-to-char">http://stackoverflow.com/questions/4708444/is-there-a-good-way-to-convert-from-unsigned-char-to-char</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/5f6c9f8h%28v=VS.80%29.aspx">http://msdn.microsoft.com/en-us/library/5f6c9f8h%28v=VS.80%29.aspx</a></li>
</ul>
<p>N&#8217;hésitez pas à les consulter pour obtenir d&#8217;autres informations. Je vous recommande également de vous inscrire sur Stack Overflow qui est à mon sens le meilleur site de questions/réponses concernant la programmation : le niveau des questions et surtout des réponses y est vraiment très élevé.</p>
<p>Comme toujours bien sûr, vous pouvez également utiliser les commentaires pour obtenir des précisions sur un point ou l&#8217;autre.</p>
<p>Merci pour votre lecture !</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/01/17/les-casts-en-cpp/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
