FAQ C++/CLI et VC++.Net
FAQ C++/CLI et VC++.NetConsultez toutes les FAQ
Nombre d'auteurs : 29, nombre de questions : 248, création le 22 février 2013
- Comment définir une classe virtuelle pure (abstract) ?
- Comment surcharger une méthode abstraite ou virtuelle (override) ?
- Comment définir une classe ou une méthode sealed ?
- Comment rompre le polymorphisme d'une fonction (new) ?
- Comment implémenter un constructeur de copie ?
- Comment implémenter opérateur d'affectation ?
- 3.4.1. Les types de données
(12)
- Qu'est-ce qu'une classe managée ?
- Qu'est-ce qu'une classe non managée ?
- Qu'est-ce qu'un type de référence ?
- Qu'est-ce qu'un type de valeur ?
- Quelles sont les équivalences des types natifs dans le framework .Net ?
- Comment créer une énumération C++/CLI ?
- Comment définir un literal ?
- Comment définir une variable initonly ?
- Comment savoir si un handle est nul ?
- 3.4.1.1. Variables et fonctions statiques (3)
- 3.4.2. Les interfaces (4)
On définit en C++/CLI une classe virtuelle pure en utilisant le mot clé sensible au contexte abstract (voir Qu'est-ce qu'une fonction virtuelle pure ? dans la faq c++).
Ainsi, l'utilisation d'abstract indique qu'une classe et ses membres abstraits peuvent être uniquement définis dans une classe dérivée et ne pourront pas être instanciés (seulement servir de classe de base).
On déclare une classe virtuelle pure ainsi :
ref class
c abstract
{
public
:
virtual
void
f() abstract;
}
;
Lien : Qu'est-ce qu'une fonction virtuelle pure ? dans la faq c++
On utilise le mot clé sensible au contexte override pour préciser explicitement que l'on surcharge une méthode (abstraite ou virtuelle).
ref class
c abstract
{
public
:
virtual
void
f() abstract;
}
;
ref class
d : c
{
public
:
virtual
void
f() override
{
// implémentation
}
}
;
Il s'agit ici d'une surcharge implicite.
On utilise le mot clé sensible au contexte sealed pour indiquer qu'une classe ou une méthode ne peut pas être dérivée.
Ainsi, dans l'exemple suivant, la méthode f de la casse c ne peut être surchargée, et provoque ainsi une erreur de compilation :
ref class
c
{
public
:
virtual
void
f() sealed
{
System::Console::
WriteLine("f ne pourra pas être dérivée"
);
}
}
;
ref class
d : public
c
{
public
:
virtual
void
f() override
// erreur du compilo
{
// ....
}
}
;
error C3764: 'd::f': cannot override base class method 'c::f'
On définit une classe sealed de la même facon :
ref class
c sealed
{
public
:
void
f()
{
// ...
}
}
;
ref class
d : public
c // erreur
{
}
;
Ici on obtiendra l'erreur de compilation suivante :
error C3246: 'd' : cannot inherit from 'c' as it has been declared as 'sealed'
On utilise le mot clé sensible au contexte new pour indiquer qu'une fonction ne surcharge pas une méthode d'une classe mère.
Ainsi l'exemple suivant :
ref class
Animal
{
public
:
virtual
void
QuiSuisJe()
{
Console::
WriteLine("Je suis un animal"
);
}
}
;
ref class
Dog : Animal
{
public
:
virtual
void
QuiSuisJe() override
{
Console::
WriteLine("Je suis un chien"
);
}
}
;
ref class
Cat : Animal
{
public
:
virtual
void
QuiSuisJe() new
{
Console::
WriteLine("Je suis un chat"
);
}
}
;
int
main()
{
Animal ^
an1 =
gcnew Animal();
Animal ^
an2 =
gcnew Dog();
Animal ^
an3 =
gcnew Cat();
an1->
QuiSuisJe();
an2->
QuiSuisJe();
an3->
QuiSuisJe();
}
produira le resultat suivant :
Je suis un animal
Je suis un chien
Je suis un animal
On constate bien que l'objet Animal (Cat) ne peut pas utiliser la méthode QuiSuisJe() de la classe Cat, celle-ci n'étant pas la surcharge de la méthode QuiSuisJe de la classe Animal, à contrario de la classe Dog.
Un constructeur de copie instancie un objet en créant une copie d'un autre objet. Alors qu'en C++, un constructeur de copie par défaut est généré automatiquement, en C++/CLI ce n'est pas le cas.
Considérons l'exemple suivant :
ref class
Personne
{
private
:
String ^
nom;
String ^
prenom;
public
:
Personne(String ^
n, String ^
p) : nom(n), prenom(p){}
Personne(const
Personne^
p) : nom(p->
nom), prenom(p->
prenom){}
}
;
// exemple d'appel
Personne ^
p1 =
gcnew Personne("nico"
, "pyright"
);
Personne ^
p2 =
gcnew Personne(p1);
Nous avons ainsi défini un constructeur de copie.
NB : Une limitation de cet exemple est qu'on ne peut utiliser le constructeur de copie qu'avec des handles d'objet, et non avec un objet lui même.
L'exemple suivant produit une erreur de compilation :
Personne p3("nico"
, "pyright"
);
Personne ^
p4 =
gcnew Personne(p3); // erreur de compilation C3073
Pour contourner ce problème, on peut utiliser l'opérateur de référence %.
Personne p3("nico"
, "pyright"
);
Personne ^
p4 =
gcnew Personne(%
p3);
NB : Une autre solution pourrait être de définir un autre constructeur de copie utilisant une référence, mais cela entraine une duplication de code.
On pourrait bien sûr déporter l'initialisation dans une méthode privée, mais cela nous priverait d'utiliser les listes d'initialisations.
Un opérateur d'affectation permet d'affecter un objet à un autre. Il est généré automatiquement en C++ natif, en C++/CLI il faut le spécifier manuellement.
Dans l'exemple précédent, l'affectation suivante :
Personne p1("nico"
, "pyright"
);
Personne p2(""
, ""
);
p2 =
p1;
Aurait généré l'erreur de compilation C2582 ('operator =' function is unavailable).
Pour y remédier, on définit l'opérateur d'affectation, comme en C++ classique :
Personne%
operator
=
(const
Personne%
p)
{
if
(%
p ==
this
)
return
*
this
;
nom =
p.nom;
prenom =
p.prenom;
return
*
this
;
}
Il ne faut bien sûr pas oublier de tester si on est pas en train d'affecter le même objet.
NB : L'opérateur d'affectation est visible uniquement par des clients C++/CLI, et non depuis des langages comme C# ou VB.Net.
On prendra garde de même à ne pas confondre avec l'affectation de handles vers les objets managés.