web-dev-qa-db-fra.com

En C ++, est-il possible de transmettre une classe comme héritant d'une autre classe?

Je sais que je peux faire:

class Foo;

mais puis-je transmettre une classe comme héritant d'une autre, comme:

class Bar {};

class Foo: public Bar;

Un exemple de cas d'utilisation serait les types de retour de référence co-variantes.

// somewhere.h
class RA {}
class RB : public RA {}

... puis dans un autre en-tête qui ne comprend pas quelque part.h

// other.h
class RA;

class A {
 public:
  virtual RA* Foo();  // this only needs the forward deceleration
}

class RB : public RA; // invalid but...

class B {
 public:
  virtual RB* Foo();  // 
}

La seule information dont le compilateur devrait avoir besoin pour traiter la déclaration de RB* B:Foo() est que RB a RA comme classe de base publique. Maintenant, il est clair que vous aurez besoin de quelque part.h si vous avez l'intention de déréférencer les valeurs de retour de Foo. Cependant, si certains clients n'appellent jamais Foo, il n'y a aucune raison pour qu'ils incluent quelque part.h, ce qui pourrait accélérer considérablement la compilation.

59
anon

Une déclaration directe n'est vraiment utile que pour indiquer au compilateur qu'une classe portant ce nom existe et sera déclarée et définie ailleurs. Vous ne pouvez pas l'utiliser dans tous les cas où le compilateur a besoin d'informations contextuelles sur la classe, et il ne sert à rien au compilateur de lui en dire un peu plus sur la classe. (Généralement, vous ne pouvez utiliser la déclaration directe que lorsque vous faites référence à cette classe sans autre contexte, par exemple en tant que paramètre ou valeur de retour.)

Ainsi, vous ne pouvez pas transmettre Bar de déclaration dans aucun scénario où vous l'utilisez ensuite pour aider à déclarer Foo, et cela n'a aucun sens d'avoir une déclaration avant qui inclut la classe de base - qu'est-ce que cela vous dit en plus rien?

40
Joe

Les déclarations avancées sont des déclarations, pas des définitions. Ainsi, tout ce qui nécessite la déclaration d'une classe (comme les pointeurs vers cette classe) n'a besoin que de la déclaration directe. Cependant, tout ce qui nécessiterait la définition - c'est-à-dire aurait besoin de connaître la structure réelle de la classe - ne fonctionnerait pas uniquement avec la déclaration directe.

Les classes dérivées doivent certainement connaître la structure de leur parent, pas seulement que le parent existe, donc une déclaration directe serait insuffisante.

39
Jonathan M Davis

Non, il n'est pas possible de transmettre l'héritage de déclaration, même si vous n'avez affaire qu'à des pointeurs. Lorsqu'il s'agit de conversions entre pointeurs, le compilateur doit parfois connaître les détails de la classe pour effectuer correctement la conversion. C'est le cas de l'héritage multiple. (Vous pouvez cas particulier certaines parties de la hiérarchie qui utilisent uniquement l'héritage unique, mais cela ne fait pas partie du langage.)

Considérez le cas trivial suivant:

#include <stdio.h>
class A { int x; };
class B { int y; };
class C: public A, public B { int z; };
void main()
{ 
    C c; A *pa = &c; B *pb = &c; C *pc = &c; 
    printf("A: %p, B: %p, C: %p\n", pa, pb, pc);
}

La sortie que j'ai reçue (en utilisant Visual Studio 2010 32 bits) est:

A: 0018F748, B: 0018F74C, C: 0018F748

Ainsi, pour l'héritage multiple, lors de la conversion entre des pointeurs liés, le compilateur doit insérer une arithmétique de pointeur pour obtenir les bonnes conversions.

C'est pourquoi, même si vous ne traitez qu'avec des pointeurs, vous ne pouvez pas transmettre l'héritage déclaré.

Quant à savoir pourquoi cela serait utile, cela améliorerait les temps de compilation lorsque vous souhaitez utiliser des types de retour co-variantes au lieu d'utiliser des transtypages. Par exemple, cela ne compilera pas:

class RA;
class A             { public: virtual RA *fooRet(); };
class RB;
class B : public A  { public: virtual RB *fooRet(); };

Mais cela:

class RA;
class A             { public: virtual RA *fooRet(); };
class RA { int x; };
class RB : public RA{ int y; };
class B : public A  { public: virtual RB *fooRet(); };

Ceci est utile lorsque vous avez des objets de type B (pas des pointeurs ou des références). Dans ce cas, le compilateur est suffisamment intelligent pour utiliser un appel de fonction direct, et vous pouvez utiliser le type de retour de RB * directement sans transtypage. Dans ce cas, généralement, je continue et je fais le type de retour RA * et je fais un cast statique sur la valeur de retour.

18
user1332054