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
La surcharge d'opérateur du C++/CLI part du même principe que pour le C++. Elle permet de tirer parti de l'intuition des utilisateurs de la classe. L'utilisateur va en effet pouvoir écrire son code en s'exprimant dans le langage du domaine plutôt que dans celui de la machine.
Cependant, il y a quelques différences. La différence majeure vient de la capacité du framework .Net d'être utilisé à travers plusieurs langages.
Ainsi, cet aspect impose que les opérateurs de surcharge en C++/CLI soient déclarés en static.
Remarque : On ne peut pas surcharger l'opérateur gcnew, alors qu'on peut surcharger new en C++ natif. C'est également le cas pour les opérateurs delete, new, [] et ().
Etant donnée que l'on peut utiliser la surcharge d'opérateur en C++ natif, pourquoi utiliser la surcharge du C++/CLI ?
Cette question revient à se poser la question de pourquoi utiliser une classe référence plutôt qu'une classe C++.
Il y a bien sur plusieurs raisons pour un tel choix, citons par exemple la gestion de la classe par le garbage collector ou la possibilité d'utiliser cette classe depuis un autre langage .Net (C#, VB.Net, etc ...).
Si l'on choisit d'implémenter une classe référence, on est obligé d'utiliser la surcharge d'opérateur du C++/CLI. Et comme une classe ref ne supporte pas les fonctions amies (friend), il devient indispensable d'utiliser des fonctions statiques pour les opérateurs binaires.
J'ai écrit une petite classe pour l'occasion qui me permet d'illustrer la surcharge de différents opérateurs. (Cette classe n'est bien sur pas complète et ne gère pas non plus les erreurs).
Il s'agit d'une classe Temps, composée d'un entier heure, d'un entier minute et d'un entier seconde. Vous l'aurez compris, nous allons additionner des Temps.
Pour cet exemple, nous surchargeons l'opérateur unaire + qui additionne deux Temps, puis les opérateurs binaires + qui additionnent un Temps et un entier représentant un nombre de secondes (et inversement). Il s'agira ensuite d'implémenter l'opérateur ++ qui ajoutera simplement une seconde à notre Temps.
ref class
Temps
{
private
:
int
heure;
int
min;
int
sec;
public
:
Temps(int
h, int
m, int
s)
{
heure =
h;
min =
m;
sec =
s;
}
virtual
String^
ToString() override
{
return
"Il est "
+
heure +
":"
+
min +
":"
+
sec;
}
Temps^
operator
+
(Temps^
t)
{
int
s =
(sec +
t->
sec)`;
int
m =
(min +
t->
min +
(sec +
t->
sec)/
60
)`;
int
h =
(heure +
t->
heure +
(min +
t->
min +
(sec +
t->
sec)/
60
)/
60
)$;
return
gcnew Temps(h, m, s);
}
static
Temps^
operator
+
(Temps^
t, int
sec);
static
Temps^
operator
+
(int
sec, Temps^
t);
static
Temps^
operator
++
(Temps^
t);
}
;
Temps^
Temps::
operator
+
(Temps ^
t, int
sec)
{
int
s =
(sec +
t->
sec)`;
int
m =
(t->
min +
(sec +
t->
sec)/
60
)`;
int
h =
(t->
heure +
(t->
min +
(sec +
t->
sec)/
60
)/
60
)$;
return
gcnew Temps(h, m, s);
}
Temps^
Temps::
operator
+
(int
sec, Temps ^
t)
{
return
t+
sec;
}
Temps^
Temps::
operator
++
(Temps^
t)
{
return
t+
1
;
}
int
main(array<
System::
String ^>
^
args)
{
Temps ^
t1 =
gcnew Temps(3
,20
, 40
);
Temps ^
t2 =
gcnew Temps(4
,50
, 20
);
Console::
WriteLine(t1+
t2);
Console::
WriteLine(t1+
10
);
Console::
WriteLine(90
+
t1);
Console::
WriteLine(++
t1);
Console::
WriteLine(t1);
}
Remarques
- Nous sommes obligés d'implémenter une surcharge avec des méthodes statiques pour les opérateurs + avec un entier et l'opérateur ++. (en effet, une classe ref ne supporte pas les classes amies (friend)).
Notez aussi que l'on définit une seule fois l'opérateur ++ et que nous pouvons écrire t1++ ou ++t1. - On implémente la surcharge de la même façon pour une classe référence ou pour une classe de valeur. La seule différence vient du fait que dans un cas, on utilise des handles ^.
Pour implémenter une propriété (un accesseur), on utilise le mot clé property. On peut soit l'avoir en lecture seule uniquement (implémentation de get uniquement), en écriture seule (implémentation de set uniquement) ou en lecture/écriture (implémentation de get et de set).
ref class
Personne
{
private
:
String ^
_nom;
String ^
_prenom;
int
^
_age;
public
:
Personne(String ^
nom, String ^
prenom, int
age) : _nom(nom), _prenom(prenom), _age(age) {}
property String ^
Nom
{
String ^
get() {
return
_nom; }
}
property String ^
Prenom
{
String ^
get() {
return
_prenom; }
void
set(String ^
value) {
_prenom =
value; }
}
property int
Age
{
void
set(int
value) {
_age =
value; }
}
virtual
String ^
ToString() override
{
return
String::
Format("Je m'appelle {0} {1} et j'ai {2} ans"
, _prenom, _nom, _age);
}
}
;
int
main()
{
Personne ^
moi =
gcnew Personne("Pyright"
, ""
, 15
);
moi->
Prenom =
"Nico"
;
moi->
Age =
28
;
// int a = moi->Age; => error C2039: 'get' : is not a member of 'Personne::Age'
Console::
WriteLine(moi->
Nom);
Console::
WriteLine(moi);
}
Une manière très simple de simplifier la propriété en lecture et écrire Prenom ci-dessus (qui affecte et retourne un membre privé) est de la remplacer par cette écriture :
property String ^
Prenom;
Il s'agit de créer une classe qui contient une propriété d'indexation par défaut :
ref class
Jours
{
private
:
array<
String^>^
lesJours;
public
:
Jours(...array<
String^>^
j) : lesJours(j) {}
property int
Length
{
int
get()
{
return
lesJours->
Length;
}
}
property String^
default
[int
]
{
String^
get(int
index)
{
if
(index >=
lesJours->
Length)
throw
gcnew Exception("Index en dehors des limites"
);
return
lesJours[index];
}
void
set(int
index, String^
elt)
{
if
(index >=
lesJours->
Length)
throw
gcnew Exception("Index en dehors des limites"
);
lesJours[index] =
elt;
}
}
}
;
int
main(array<
System::
String ^>
^
args)
{
Jours ^
lesjours =
gcnew Jours("Lundi"
, "Mardi"
, "Mercredi"
, "Jeudi"
, "Vendredi"
, "Samedi"
, "Dimance"
);
lesjours[3
] =
"Jeudi c'est permis"
;
for
(int
i =
0
; i <
lesjours->
Length; i++
)
Console::
WriteLine("Le jour {0} est {1}"
, i+
1
, lesjours[i]);
}