Pages

mercredi 6 février 2013

Appel explicite aux membres d'une classe

Ce qui suit permet d'appeler les attributs/fonctions cachés du fait d'un héritage ou faire appel à la fonction virtuelle d'un parent même si celle-ci est redéfinie dans un fil.

Ce principe est plus connu à l'intérieur d'une classe est utilisé avec l’héritage:

struct A
{
 virtual int f() { return 3; }
 virtual ~A(){}
};

struct B : A
{
 virtual int f()
 { return A::f() + 3; } // Utilise la fonction f() du parent
};

Mais il peut aussi s'utiliser à l'extérieur d'une classe. Le A::f() fait référence à une fonction de l'objet lié au pointeur this. Donc this->A::f() est syntaxiquement valide. Ce qui veut dire qu'on peut faire référence à n'importe quel membre contenu dans l'objet.

struct A { int n = 2; };
struct B : A { int n = 3; };

B b;
b.n == 3
b.A::n == 2
struct C { virtual int f(){ return 2; } };
struct D : C { int f(){ return 3; } };

D d;
b.f() == 3
b.C::f() == 2

Cette technique est utilisé pour par exemple changer le buffer d'un fstream. La méthode std::ios::rdbuf(std::streambuf*) étant cacher par std::ofstream::rdbuf() c'est un moyen plus simple que le cast en une référence sur std::ios.

std::ofstream os("...");
//...
static_cast<std::ios&>(os).rdbuf(buf);
//ou
os.std::ios::rdbuf(buf);

1 commentaire:

Blanchet Florian a dit…

Bonjour,

Pour aller plus loin, de manière général qualifier un nom (ie le faire précéder de ::) à tendance à désactiver plusieurs mécanismes :

- Dans ton article le polymorphisme
- La decay conversion, ca explique qu'on peut faire foo pour passer une fonction à une autre mais qu'on doit faire &A::foo si la fonction est membre.
- Le lookup

Et probablement d'autre cas. La conclusion étant donc : ne qualifier vos noms que si vous savez ce que vous faites. En effet les mécanismes du C++ sont là dans l'esprit du principe de moindre surprise, s'en affranchir c'est prendre des risques.