Télécharger la présentation
1
Notions avancées du C++
Downcasting, Héritage multiple, et autres joyeusetés…
2
Downcasting Transtypage descendant Pas naturel Depuis la classe de base vers la classe dérivée Exemple avec des figures géométriques Parfois nécessaire mais s'il y en a beaucoup : conception à revoir
3
Downcasting Un cercle est-une figure Un carré est-une figure
class Figure { points3D *contour; long nbPointsContour; public : virtual void dessiner()=0; virtual void effacer()=0; virtual Figure *symetrique(/*const Droite &*/); }; class Cercle : public Figure {}; class Carre : public Figure
4
Downcasting Utilisation de l'opérateur dynamic_cast Syntaxe :
int main(int argc, char *argv[]) { Figure *f = new Cercle(…); // avec les bons paramètres Figure *g = f->symetrique(); // traiter g comme un cercle ou un carré ? return 0; } Utilisation de l'opérateur dynamic_cast Syntaxe : dynamic_cast<type_vers_lequel_transtyper>(Expression); Retourne l'expression transtypée.
5
dynamic_cast<>
int main(int argc, char *argv[]) { Figure *f = new Carre(…); // avec les bons paramètres Figure *g = f->symetrique(); Carre *c = dynamic_cast<Carre *>(g); if (c==NULL) // le transtypage a échoué // on pourrait lancer une exception } else c->afficheLeCote(); return 0;
6
dynamic_cast<>
Ne fonctionne qu'avec références et pointeurs : utilisé avec l'héritage et le polymorphisme Vérification effectuée à l'exécution (runtime) Overhead du au mécanisme de vérification dynamic_cast<> retourne NULL si le transtypage n'est pas possible avec des pointeurs Avec des références : exception std::bad_cast lancée : il faut utiliser try…catch !
7
dynamic_cast<> et références
int main(int argc, char *argv[]) { Figure *f = new Carre(…); // avec les bons paramètres Figure *g = f->symetrique(); try Cercle &circ = dynamic_cast<Cercle &>(*g); } catch (std::bad_cast bc) cout << "argh :" << bc.what() << endl; return 0; argh : St8bad_cast
8
static_cast<> Conversion explicite sans contrôle au runtime
Toujours effectué : résultats dangereux Plus rapide que dynamic_cast<> À utiliser quand on connaît exactement le type à obtenir
9
Dangers de static_cast<>
class Cercle : public Figure { long r; public : Cercle():r(2) {} void dessiner() cout << "cercle::dessin()" << endl; } figure *symetrique() // simplifié ici return new Cercle(); long rayon(){return r;} };
10
Dangers de static_cast<>
class Carre : public figure { double _dum; long c; // alignement différent de Cercle public : Carre():c(5) {} void dessiner() cout << "carre::dessiner()" << endl; } figure *symetrique() return new Carre(); long cote() return c; };
11
Dangers de static_cast<>
int main(int argc, char* argv[]) { Figure *fig = new Carre(); Figure *sym = fig->symetrique(); // donc un carré Carre *ca = static_cast<Carre *>(sym); cout << ca->cote() << endl; // allons-y gaiement Cercle *ci = static_cast<Cercle *>(sym); cout << ci->rayon() << endl; return 0; } Ok, c'est le bon type Ca passe à la compilation car c'est du static_cast 5
12
Explication 8 octets pour un double sym 4octets pour un long ca
Figure *sym = fig->symetrique(); Carre *ca = static_cast<Carre *>(sym); cout << ca->cote() << endl; Cercle *ci = static_cast<Cercle *>(sym); cout << ci->rayon() << endl; ca Rappel : dans Carre : un double, un long dans Cercle : un long ci Ce que "voit" ci ci 4 octets pour un long
13
Héritage multiple et virtuel
Une classe peut hériter de plusieurs classes Parfois nécessaire Mais attention au losange ! Classe A Classe B Classe C Classe D
14
Membres de A Par héritage, les membres de A sont présents dans B et C, et donc dans D, mais…plusieurs fois ! Un attribut peut donc être ambigu ! On peut : Lever l'ambiguïté en qualifiant l'attribut Utiliser l'héritage virtuel
15
Exemple class A { protected : string s; public : A(const string &_s="A"):s(_s){cout << "A::A(" << s<<")"<< endl;} }; class B : public A public : B(const string &_s="B"):A(_s){cout << "B::B(" << s<<")"<< endl;} void f() {cout << s << endl;} class C : public A public : C(const string &_s="C"):A(_s){cout << "C::C(" << s<<")"<< endl;}
16
Exemple Indiquer de quelle classe il s'agit : A, B ou C ?
class D : public B, public C { public : D():B("DB"),C("DC"){cout << "D::D(" << s <<")"<< endl;} void f() cout << s << endl; } }; Indiquer de quelle classe il s'agit : A, B ou C ? A::s Reference to 's' is ambiguous 'A' is an ambiguous base of 'D'
17
Exemple : choix de B Pourquoi celui-là ? A::A(DB) B::B(DB) A::A(DC)
class D : public B, public C { public : D():B("DB"),C("DC"){cout << "D::D(" << B::s <<")"<< endl;} void f() cout << B::s << endl; } }; Pourquoi celui-là ? dans main() : D d; A::A(DB) B::B(DB) A::A(DC) C::C(DC) D::D(DB)
18
Héritage Virtuel class B : virtual public A {…}; class C : virtual public A {…}; Ainsi, les membres de A ne sont plus dupliqués dans D ! D d; Mais il y a un prix à payer ! A::A(A) B::B(A) C::C(A) D::D(A)
19
Héritage Virtuel class B : virtual public A {…}; class C : virtual public A {…}; Ainsi, les membres de A ne sont plus dupliqués dans D ! D d; Mais il y a un prix à payer ! A::A(A) B::B(A) C::C(A) D::D(A) On ne peut plus faire de downcasting statique avec un héritage virtuel ! On doit utiliser dynamic_cast<>
20
Les foncteurs Objet et fonction en même temps
Par surcharge de l'opérateur () : accepte plusieurs paramètres Permet de réaliser des pointeurs de fonction Passe des paramètres à l'objet
21
Foncteurs simple Exemple à vertu pédagogique Dans main() : Affiche 3…
class X { public : void operator()(long p) cout << p << endl; } }; Dans main() : X x; x(3); Affiche 3…
22
Foncteurs moins simple (mais mieux)
class action { char _optype; public : action(const char op='+'):_optype(op) {} int operator()(int a, int b) switch(_optype) case '+' : return a+b; case '-' : return a-b; default : return 0; } }; Le constructeur L'opérateur ()
23
Foncteurs moins simple (mais mieux)
int main(int argc, char *argv[]) { action a; action b('-'); int x = a(4,3); cout << x << " et " << b(x,a(1,1)) << endl; return 0; }
24
Foncteur carrément moins simple (mais carrément mieux)
template<class T> class action { char _optype; public : action(const char op='+'):_optype(op) {} T operator()(T a, T b) swith(_optype) case '+' : return a+b; case '-' : return a-b; default : return 0; } }; Seule contrainte : T doit implémenter operator+, operator-
25
Foncteur carrément moins simple (mais carrément mieux)
int main(int argc, char *argv[]) { action<int> a; action<int> b('-'); action<string> c; int x = a(4,3); cout << x << " et " << b(x,a(1,1)) << endl; cout << c("hello ", "world++"); return 0; } On peut même étendre avec un troisième paramètre indiquant l'opération à effectuer !
26
Foncteur et STL Parcours de conteneur avec un itérateur : boucle for
Appliquer une fonction (un traitement) à tous les objets stockés dans un conteneur : fonction for_each (du namespace std) std::for_each(début de conteneur, fin de conteneur, opération); Opération est une fonction qui sera appelée, avec en paramètre un objet issu du conteneur
27
Foncteur et STL Applique la fonction f à tous les objets string de vec
void f(string &s) { cout << s << endl; } int main(int argc, char *argv[]) vector<string> vec; vec.push_back("un"); vec.push_back("deux"); vec.push_back("trois"); std::for_each(vec.begin(), vec.end(), f); return 0; Applique la fonction f à tous les objets string de vec
28
Foncteur et STL On voudrait diriger ces objets vers un flux quelconque (std::cout ou fichier). La fonction f aurait deux paramètres : pas compatible avec for_each : on utilise un foncteur ! class StringWriter { ostream& _flow; public : StringWriter(ostream& fl):_flow(fl) {} void operator()(const string &s) _flow << s; } };
29
Foncteur et STL On associe le flot à traiter en appelant le constructeur de StringWriter, le paramètre est traité par l'opérateur() de la classe StringWriter int main(int argc, char *argv[]) { vector<string> vec; vec.push_back("un"); vec.push_back("deux"); vec.push_back("trois"); std::for_each(vec.begin(), vec.end(), StringWriter(std::cout)); return 0; } Paramètre du constructeur, pas de l'appel à l'opérateur ()
30
Foncteur et STL Extension de la classe Writer class AnyWriter {
ostream& _flow; public : AnyWriter(ostream& fl):_flow(fl) {} template<class T> void operator()(const T &towrite) _flow << towrite; } };
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.