2012-04-02 11 views
13

considerare due puntatoricome verificare se due puntatori puntano allo stesso oggetto o no?

A* a; 
B* b; 

Sia A e B sono classi polimorfiche. Come verificare se a e b puntano allo stesso oggetto o no?

Più precisamente, specifichiamo a e b il punto sullo stesso oggetto se esiste qualche oggetto d di tipo D tale che sia * a che * b siano da qualche parte nella gerarchia di classi di d.

proporrei la seguente soluzione:

dynamic_cast<void*>(a) == dynamic_cast<void*>(b) 

Infatti, secondo lo standard,

dynamic_cast<void*>(v) 

rese “un puntatore all'oggetto massima derivazione puntato da v (n3242.pdf. : § 5.2.7 - 7). Se il più derivato per entrambi è lo stesso oggetto, i puntatori puntano allo stesso oggetto.

Sono abbastanza sicuro che dovrebbe sempre funzionare correttamente dal punto di vista pratico. Ma teoricamente, a prima vista l'uguaglianza proposta sembra produrre falsi positivi, per esempio, nel caso in cui b punti al primo membro di A (non all'antenato di A). Sebbene sia praticamente impossibile ottenere indirizzi uguali per A e il suo membro poiché il puntatore della tabella virtuale di A deve essere posizionato prima di questo membro, lo standard non impone tabelle virtuali e non dice nulla sul layout della classe.

Quindi, le mie domande sono:

  1. E 'la soluzione proposta corretta dal punto di vista di serie?

  2. Sono presenti avvertenze sull'ereditarietà privata (protetta) o sulla qualifica di cv?

  3. Ci sono soluzioni migliori?

[EDIT]

ho cercato di presentare alcuni esempio che illustra uno scenario relativamente complessa. In questo caso il crosscasting dinamico e il casting statico sono ambigui.

// proposed impplementation: 
template<typename P, typename Q> 
bool test_ptrs(const P* p, const Q* q) 
{ 
    return (dynamic_cast<const void*>(p) == dynamic_cast<const void*>(q)); 
} 


struct Root 
{ 
    virtual ~Root(){}; 
}; 

struct A: public Root // nonvirtually 
{ 
}; 

struct B: public Root // nonvirtually 
{ 
}; 

struct C: public A, B // nonvirtual diamond started with Root 
{ 
    Root another_root_instance; 
}; 

int main() 
{ 
    C c; 

    A* pa= &c; 
    B* pb= &c; 

    bool b = (dynamic_cast<void*>(pa) == dynamic_cast<void*>(pb)); 

    Root* pra= dynamic_cast<Root*> (pa); 
    Root* prb= dynamic_cast<Root*> (pb); 

    //Root* prc= dynamic_cast<Root*> (&c); // runtime error, ambiguous cast 
    Root* prr= dynamic_cast<Root*>(pra); 

    Root* pcar= dynamic_cast<Root*>(pra); 
    Root* pcbr= dynamic_cast<Root*>(prb); 

    if(
     test_ptrs(pa, pb) 
     && test_ptrs(pra, prb) 
     && !test_ptrs(pa,&c.another_root_instance) 
    ) 
    { 
    printf("\n test passed \n"); 
    } 
} 
+11

Perché non 'a == b'? – iammilind

+1

@iammilind: A e B possono essere classi base per alcuni D ma non correlate tra loro – user396672

+2

+1 per @iammilind - i vecchi 'uns sono i migliori! –

risposta

1

stavo cercando di risolvere questo confrontando l'indirizzo questi puntatori che punta a.

  • Indirizzo indica le modifiche in base al tipo di puntatore.

Quindi teoricamente possiamo dire come

a * eb punti * allo stesso oggetto se esiste qualche oggetto c di tipo C tale che sia * a e * b sono da qualche parte nella classe gerarchia di C."

Logicamente

dobbiamo rivisitare la dichiarazione di cui sopra come " a * eb punti * allo stesso oggetto ma ha lo propria zona di accesso nella memoria di obj c di tipo C in modo tale che sia * a * b e sono da qualche parte nella gerarchia delle classi di C. ""

struct Aa { int a; Aa() {a= 0;} };

struct Bb 
{ int b; 
    Bb() { b= 0;} 
}; 
struct C: Aa, Bb {  
}; 

C c; 
Aa *a1 = &c; 
Aa *a2 = &c; 
Bb *b1 = &c; 
Bb *b2 = &c; 

cout << &c << "\t"<< &(*a1)<<"\t"<< &(*a2)<<endl; 
cout << &c << "\t"<< &(*b1)<<"\t"<< &(*b2)<<endl; 

uscita:

  • & c 0x0012fd04
  • & (* a1) 0x0012fd04
  • & (* a2) 0x0012fd04
  • & (* b1) 0x0012fd08
  • & (* b2) 0x0012fd08

Anche se questo non risolverà il tuo problema, abbiamo un punto di dedurre qui.

1

Sembrerebbe a me il modo meno puzzolente per affrontare questo è quello di introdurre una classe base per A & B:

#include <iostream> 

struct Base 
{ 
    virtual ~Base() {}; 
}; 

struct A : public virtual Base 
{ 
    int a; 
    virtual ~A() {}; 
    virtual void afunc() {}; 
}; 



struct B : public virtual Base 
{ 
    int b; 
    virtual ~B() {}; 
    virtual void bfunc() {}; 
}; 

struct C: A, B 
{}; 

int main() 
{ 
    C c; 
    A *a = &c; 
    B *b = &c; 

    std::cout << "a* == " << &(*a) << std::endl; 
    std::cout << "b* == " << &(*b) << std::endl; 
    std::cout << "a == b == " << ((void*)a == (void*)b) << std::endl; 

    Base* ba = a; 
    Base* bb = b; 

    std::cout << "ba* == " << &(*ba) << std::endl; 
    std::cout << "bb* == " << &(*bb) << std::endl; 
    std::cout << "ba == bb == " << (ba == bb) << std::endl; 

    return 0; 
} 
+0

Concordo sul fatto che sia meno puzzolente, anche se, in senso stretto, non è una risposta :). – user396672

0

Dal momento che con dynamic_cast È anche possibile convertire "di traverso" nella gerarchia di tipo, vorrei suggerire:

(b != nullptr? dynamic_cast<B*>(a) == b : a == nullptr) 

If a punti ad alcuni sotto-oggetto all'inizio del *b, poi dynamic_cast<B*>(a) sarà necessariamente restituire un puntatore nullo (perché non c'è modo di 012.contiene se stesso). Pertanto se b non è un puntatore nullo, dynamic_cast<B*>(a) == b riuscirà solo se entrambi condividono la stessa classe derivata. Il caso in cui b è un puntatore nullo deve essere trattato in modo specifico perché se a non è nullo, ma non punta a una classe derivata da B, il test dynamic_cast avrà esito negativo.

Tuttavia ci sono alcune situazioni che coinvolgono l'ereditarietà multipla in cui questa soluzione fornisce un falso negativo (a differenza della soluzione che non fornisce mai falsi negativi, ma può dare falsi positivi). Tuttavia le gerarchie di classi in cui ciò potrebbe accadere sono gerarchie che direi che non dovresti creare comunque (cioè la stessa classe derivata che contiene più basi indirette di tipo B). È possibile ridurre il numero di falsi negativi eseguendo nuovamente il test tramite lo scambio di ruolo a e b (quindi solo se siaA e B sono ambigui nella classe più derivata, il test non riuscirà).

Si potrebbe anche combinare la vostra e la mia prova che invia tre risultati:

  • Entrambi i test hanno successo: I puntatori sicuramente sono allo stesso oggetto (o null Noth).
  • Entrambi i test falliscono: i puntatori sicuramente non si riferiscono allo stesso oggetto.
  • Solo il mio test ha esito negativo: O il il test ha fornito un falso positivo, o il mio test ha fornito un falso negativo. Non si può dire con certezza se entrambi siano lo stesso oggetto, ma almeno si può dire che non si può dire.
Problemi correlati