Domanda

So che posso fare:

class Foo;

ma posso avanti dichiarare una classe come eredita da un altro, come:

class Bar {};

class Foo: public Bar;

Un esempio di caso di uso sarebbe tipi restituiti riferimento co-varianti.

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

... e poi in un altro colpo di testa che non include somewhere.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();  // 
}

L'unica informazione che il compilatore dovrebbe necessario elaborare la dichiarazione di RB* B:Foo() è che RB ha RA come classe base pubblica. Ora chiaramente si avrebbe bisogno somewhere.h se avete intenzione di fare qualsiasi tipo di dereferencing dei valori di ritorno da Foo. Tuttavia, se alcuni clienti non chiama mai Foo, allora non c'è motivo per loro di includere somewhere.h che potrebbe accelerare in modo significativo la compilazione.

È stato utile?

Soluzione

Una dichiarazione in avanti è realmente utile solo per dire al compilatore che una classe con quel nome esiste e verrà dichiarata e definita altrove. Non è possibile utilizzare in ogni caso in cui il compilatore ha bisogno di informazioni contestuali sulla classe, né è di alcuna utilità per il compilatore per dire solo un po 'di classe. (In generale, è possibile utilizzare solo la dichiarazione in avanti quando si parla di quella classe, senza altro contesto, per esempio come un parametro o valore di ritorno.)

In questo modo, non si può dichiarare avanti Bar in qualsiasi scenario in cui quindi si utilizza per aiutare dichiarare Foo, e flat-out non ha senso avere una dichiarazione anticipata che include la classe di base - che cosa fa che dirvi oltre niente?

Altri suggerimenti

dichiarazioni a termine sono le dichiarazioni, non definizioni. Quindi, tutto ciò che richiede la dichiarazione di una classe (come puntatori a quella classe) hanno bisogno solo la dichiarazione in avanti. Tuttavia, tutto ciò che richiederebbe la definizione - vale a dire avrebbe bisogno di conoscere la struttura attuale della classe -. Non avrebbe funzionato solo con la dichiarazione in avanti

Le classi derivate sicuramente bisogno di conoscere la struttura del loro genitore, non solo che esiste il genitore, in modo da una dichiarazione anticipata sarebbe insufficiente.

No, non è possibile inoltrare dichiarare eredità, anche se siete solo a che fare con i puntatori. Quando si tratta di conversioni tra i puntatori, a volte il compilatore deve conoscere i dettagli della classe per eseguire la conversione in modo corretto. Questo è il caso di ereditarietà multipla. (Si potrebbe caso particolare alcune parti parti della gerarchia che utilizzano solo l'ereditarietà singola, ma che non è parte del linguaggio).

Si consideri il seguente caso banale:

#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);
}

L'output che ho ricevuto (utilizzando 32 bit Visual Studio 2010), è il seguente:

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

Quindi, per ereditarietà multipla, quando la conversione tra puntatori relativi, il compilatore deve inserire qualche aritmetica dei puntatori per ottenere le conversioni a destra.

Questo è il motivo per cui, anche se avete a che fare solo con i puntatori, non è possibile dichiarare in avanti l'eredità.

Per quanto riguarda il motivo per cui sarebbe utile, sarebbe migliorare i tempi di compilazione quando si vuole fare uso di co-variante tipi restituiti invece di utilizzare calchi. Ad esempio, questo non verrà compilato:

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

Ma questa volontà:

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(); };

Questo è utile quando si dispone di oggetti di tipo B (non puntatori o riferimenti). In questo caso il compilatore è abbastanza intelligente per utilizzare una chiamata di funzione diretta, ed è possibile utilizzare il tipo di ritorno di RB * direttamente senza fusione. In questo caso, di solito mi andare avanti e fare il tipo di ritorno RA * e fare un cast statico sul valore di ritorno.

Non credo che sia utile. Considerate: è stata definita una classe, Bar:

class Bar {
public:
    void frob();
};

Ora si dichiara una classe Foo:

class Foo;

Tutto quello che puoi fare con Foo è costruire un puntatore ad esso. Ora, si supponga di aggiungere le informazioni che Foo è derivato da Bar:

class Foo: public Bar;

Che cosa si può fare ora che non si poteva fare prima? Credo che tutto si può fare è accettare un puntatore a Foo e gettato in un puntatore a Bar, quindi utilizzare tale puntatore.

void frob(Foo* f) {
    Bar *b = (Bar)f;
    b->frob();
}

Tuttavia, è necessario aver generato il puntatore altrove, quindi si potrebbe avere appena accettato un puntatore a Bar invece.

void frob(Bar* b) {
    b->frob();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top