Divertimento multi ereditario con puntatore C ++

Sto scrivendo un codice che riguarda l’ereditarietà da una class di puntatori di conteggio di riferimento di base; e alcune complicazioni di C ++ sono spuntate. L’ho ridotto come segue:

Supponiamo di avere:

class A{}; class B{}; class C: public A, public B {}; C c; C* pc = &c; B* pb = &c; A* pa = &c; // does pa point to a valid A object? // does pb point to a valid B object? // does pa == pb ? 

Inoltre, fa:

 // pc == (C*) pa ? // pc == (C*) pb ? 

Grazie!

  • fa riferimento a un object A valido?
  • punta pb su un object B valido?

Sì, il C* viene convertito in modo che pa e pb puntino agli indirizzi corretti.

  • fa pa == pb?

No, di solito no. Non può esserci un object A e un object B allo stesso indirizzo.

Inoltre, fa

  • pc == (C *) pa?
  • pc == (C *) pb?

Il cast converte i puntatori di nuovo nell’indirizzo dell’object C , quindi entrambe le uguaglianze sono vere.

C incorpora un A e un B

 class C: public A, public B {}; 

è molto simile al codice C.

 struct C { A self_a; B self_b; }; 

e (B*) &c; è equivalente a static_cast< B* >( &c ) è simile a &c.self_b se stai usando una C.

In generale, non è ansible fare affidamento su puntatori a tipi diversi essendo intercambiabili o comparabili.

 pc == pa; pc == pb; 

Non definito, dipende dalla struttura della class.

 pc == (C*) pa; pc == (C*) pb; 

Va bene.

 pa == pb; 

No.

Indicano oggetti validi?

 Yes 

Articolo 28 Significato del confronto tra puntatori in C ++ Common Knowledge: Essential Intermediate Programming ) spiega la chiave del puntatore dell’object in C ++:

In C ++, un object può avere più indirizzi validi e il confronto tra puntatori non è una domanda sugli indirizzi. È una domanda sull’id quadro dell’object.

Dai un’occhiata al codice:

 class A{}; class B{}; class C: public A, public B {}; C c; C* pc = &c; B* pb = &c; A* pa = &c; 

class C deriva sia dalla class A che dalla class B , quindi la class C è sia di class A che di class B l’object C c ha 3 indirizzi validi: indirizzo per class A , class B e class C L’implementazione dipende dal compilatore, quindi non puoi assumere il layout di memoria della class C , e potrebbe piacere questo:

  ---------- <- pc (0x7ffe7d10e1e0) | | ---------- <- pa (0x7ffe7d10e1e4) | A data | ---------- <- pb (0x7ffe7d10e1e8) | B data | ---------- | C data | ---------- 

Nel caso precedente, sebbene il valore dell'indirizzo di pc , pa e pb non siano gli stessi, si riferiscono tutti allo stesso object ( c ), quindi il compilatore deve assicurarsi che il pc sia uguale a pa e pb , ovvero pc == pa e pc == pb . Il compilatore esegue questo confronto regolando il valore di uno dei puntatori confrontati dall'offset appropriato. Per esempio,

 pc == pa 

è tradotto in:

 pc ? ((uintptr_t)pc + 4 == (uintptr_t)pa) : (pa == 0) 

Tra l'altro, poiché A e B non hanno alcuna relazione di ereditarietà, non possiamo confrontare direttamente pa e pb .

Per le tue domande:

 (1) does pa point to a valid A object? (2) does pb point to a valid B object? Yes, refer the above diagram. (3) pc == (C*) pa ? (4) pc == (C*) pb ? Yes, No need to add (C*). (5) does pa == pb ? No. We can't compare them. 

Quello che ottieni è qualcosa di simile nella memoria

  ---------- | A data | ---------- | B data | ---------- | C data | ---------- 

Quindi se vuoi l’intero object C otterrai un puntatore all’inizio della memoria. Se si desidera solo la “parte”, si ottiene lo stesso indirizzo poiché è lì che si trovano i membri dei dati. Se vuoi la “parte” B hai l’inizio + sizeof (A) + sizeof (qualunque sia il compilatore per vtable). Quindi, nell’esempio, pc! = Pb (potrebbe essere pc! = Pa) ma pa non è mai uguale a pb.