2012-04-02 8 views
13

Betrachten wir zwei Zeigerwie überprüft man, ob zwei Zeiger auf dasselbe Objekt zeigen oder nicht?

A* a; 
B* b; 

Sowohl A als auch B sind polymorph Klassen. Wie überprüft man, ob a und b auf dasselbe Objekt zeigen oder nicht?

Genauer gesagt, geben wir a und b auf dasselbe Objekt, wenn ein Objekt d vom Typ D existiert, so dass sowohl * a als auch * b irgendwo in der Klassenhierarchie von d stehen.

würde ich die folgende Lösung vor:

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

der Tat nach der Norm,

dynamic_cast<void*>(v) 

yields „ein Zeiger auf den meisten abgeleitetes Objekt durch v zeigte (n3242.pdf. : § 5.2.7 - 7). Wenn das am meisten abgeleitete für beide dasselbe Objekt ist, zeigen die Zeiger auf dasselbe Objekt.

Ich bin mir ziemlich sicher, dass es aus der praktischen Sicht immer richtig funktionieren sollte. Aber theoretisch scheint die vorgeschlagene Gleichheit auf den ersten Blick falsch positiv zu sein, zum Beispiel wenn b auf das erste Mitglied von A zeigt (nicht auf A's Vorfahren). Obwohl es praktisch unmöglich ist, gleiche Adressen für A und sein Mitglied zu erhalten, da sich der virtuelle Tabellenzeiger von A vor diesem Mitglied befinden sollte, schreibt der Standard keine virtuellen Tabellen vor und sagt nichts über das Klassenlayout aus.

Also, meine Fragen sind:

  1. ist die vorgeschlagene Lösung aus dem Standard-Sicht richtig?

  2. Gibt es irgendwelche Vorbehalte gegenüber privater (geschützter) Erbschaft oder CV-Qualifikation?

  3. Gibt es bessere Lösungen?

[EDIT]

Ich habe versucht, einige Beispiele zu präsentieren, die ein relativ komplexes Szenario veranschaulicht. In diesem Fall sind dynamisches Crosscasting und statisches Casting mehrdeutig.

// 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

Warum nicht 'a == b '? – iammilind

+1

@iammilind: A und B können Basisklassen für einige D sein aber nicht miteinander verwandt – user396672

+2

+1 für @iammilind - die alten 'uns sind die besten! –

Antwort

1

Ich habe versucht, dies zu lösen, indem Sie die Adresse, auf die diese Zeiger zeigen, vergleichen.

  • Adresse zeigt auf Änderungen basierend auf dem Typ des Zeigers.

daher theoretisch können wir sagen, wie

a * und b * Punkte auf das gleiche Objekt, wenn es c vom Typ C einen Gegenstand existiert, so dass beide * a und * b irgendwo in der Klasse Hierarchie von C.„

Logischer

wir die obige Aussage wie zu überdenken haben“ a * und b * Punkte auf das gleiche Objekt aber es eigene Zone der Zugriff im Speicher von obj c hat von Typ C, so daß sowohl * a * b und irgendwo in der Klassenhierarchie von C. sind ""

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; 

Output:

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

Obwohl dies Ihr Problem nicht lösen wird, haben wir hier einen Anhaltspunkt.

1

Es scheint mir, gelinde stinkende Weise damit zu umgehen ist eine Basisklasse für A & B vorstellen:

#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

Ich stimme zu, dass es am wenigsten stinkt, obwohl es streng genommen keine Antwort ist :). – user396672

0

Da mit dynamic_cast Sie können auch werfen „seitwärts“ in der Typenhierarchie, ich würde vorschlagen:

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

Wenn a auf ein leichtes subobject am Anfang *b, dann dynamic_cast<B*>(a) notwendigerweise einen null-Zeiger zurückgeben (weil es keine Möglichkeit gibt, ein 012.enthält sich selbst). Wenn also b kein Null-Zeiger ist, ist dynamic_cast<B*>(a) == b nur dann erfolgreich, wenn beide dieselbe abgeleitete Klasse teilen. Der Fall, dass b ein Nullzeiger ist, muss speziell behandelt werden, denn wenn a nicht null ist, aber nicht auf eine von B abgeleitete Klasse zeigt, wird der dynamic_cast Test fehlschlagen.

Es gibt jedoch Situationen mit Mehrfachvererbung, bei denen diese Lösung ein falsches Negativ ergibt (im Gegensatz zu Ihrer Lösung, die niemals falsch negative Ergebnisse liefert, aber falsche positive Ergebnisse liefern kann). Die Klassenhierarchien, in denen dies möglich ist, sind jedoch Hierarchien, die Sie sowieso nicht erstellen sollten (nämlich dieselbe abgeleitete Klasse, die mehrere indirekte Basen vom Typ B enthält). Sie können die Anzahl der falsch negativen Ergebnisse reduzieren, indem Sie die Rolle a und b erneut testen (nur dann, wenn sowohlA als auch B in der abgeleiteten Klasse mehrdeutig sind, wird der Test fehlschlagen).

Sie auch Ihren und meinen Test kombinieren könnten drei Ergebnisse geben:

  • Beiden Tests erfolgreich: Die Zeiger auf jeden Fall auf das gleiche Objekt (oder Noth null).
  • Beide Tests fehlschlagen: Die Zeiger sind definitiv nicht auf das gleiche Objekt.
  • Nur mein Test schlägt fehl: Entweder Ihr Test hat eine falsch positive, oder mein Test hat eine falsche negative gegeben. Sie können nicht sicher sagen, ob beide das gleiche Objekt sind, aber zumindest können Sie sagen, dass Sie nicht sagen können.