2009-11-19 17 views
10

Eventuali duplicati:
What’s the right way to overload operator== for a class hierarchy?uguaglianza di prova per le classi derivate in C++

In C++, come possono le classi derivate ignorare il test di uguaglianza classe base in modo significativo?

Ad esempio, supponiamo di avere una classe di base A. Le classi B e C derivano da A. Ora dati due puntatori a due oggetti A, posso verificare se sono uguali (inclusi eventuali dati di sottoclasse)?

class A { 
    public: int data; 
}; 

class B : public A { 
    public: float more_data; bool something_else; 
}; 

class C : public A { 
    public: double more_data; 
}; 


    A* one = new B; 
    A* two = new B; 
    A* three = new C; 

    //How can I test if one, two, or three are equal 
    //including any derived class data? 

C'è un modo pulito per farlo? Qual è la mia migliore scommessa?

Grazie!

+2

Possibile duplicato: http://stackoverflow.com/questions/1691007/whats-the-right-way-to-overload-operator-for-a-class-hierarchy –

+0

Vuoi confrontare 'T == T ', dove' T' potrebbe essere 'A',' B', o 'C' (fine), o vuoi confrontare' A' con 'B' e' A' con 'C' e' B' con 'C' (discutibile)? – sbi

+0

Nell'esempio sopra, voglio confrontarne uno con due e tre. Sono tutti puntatori. – Imbue

risposta

12

Ricordo di aver letto una breve descrizione dell'idioma pubblico non virtuale/non pubblico-virtuale e dei suoi vantaggi, ma non dove. This wikibook ha una descrizione a posto.

Ecco come lo si applica a op ==:

struct A { 
    virtual ~A() {} 

    int a; 

    friend 
    bool operator==(A const& lhs, A const& rhs) { 
    return lhs.equal_to(rhs); 
    } 
    // http://en.wikipedia.org/wiki/Barton-Nackman_trick 
    // used in a simplified form here 

protected: 
    virtual bool equal_to(A const& other) const { 
    return a == other.a; 
    } 
}; 

struct B : A { 
    int b; 

protected: 
    virtual bool equal_to(A const& other) const { 
    if (B const* p = dynamic_cast<B const*>(&other)) { 
     return A::equal_to(other) && b == p->b; 
    } 
    else { 
     return false; 
    } 
    } 
}; 

struct C : A { 
    int c; 

protected: 
    virtual bool equal_to(A const& other) const { 
    if (C const* p = dynamic_cast<C const*>(&other)) { 
     return A::equal_to(other) && c == p->c; 
    } 
    else { 
     return false; 
    } 
    } 
}; 
1

Un modo per farlo è quello di utilizzare il virtual operator== che prende l'oggetto di classe di base come parametro in modo che funzioni correttamente con diversi oggetti derivati . Tuttavia, è necessario rendere questa funzione pura virtuale in modo da forzare tutti gli oggetti derivati ​​per implementarla. Quindi non sarai in grado di creare un'istanza della classe base. Ad esempio:

class A 
{ 
public: 
    virtual ~A(){} 

    //A virtual operator for comparison 
    virtual bool operator==(const A& a) = 0; 

protected: 
    bool compareBase(const A& a); 

private: 
    int m_data; 
}; 

bool A::compareBase(const A &a) 
{ 
    return m_data == a.m_data; 
} 

class B1 : public A 
{ 
public: 

    //Override the base class 
    bool operator==(const A& a); 

private: 
    bool compare(const B1* pB) 
    { 
     if(compareBase(*pB)) 
     { 
      //Code for compare 
      return true; 
     } 

     return false; 
    } 
}; 

bool B1::operator ==(const A &a) 
{ 
    //Make sure that the passed type is same 
    const B1* pB = dynamic_cast<const B1*>(&a); 
    if(pB) 
    { 
     return compare(pB); 
    } 

    return false; 
} 
//Similarly implement for B2 
+0

Avere un operatore virtuale pubblico non puro == in una classe base è potenzialmente molto pericoloso in quanto non si hanno protezioni o avvisi per le nuove classi derivate che dimenticano di sovrascriverle e terminano tutte le comparazioni uguali se solo le parti di base sono uguali. –

+0

Sì, hai ragione. Modificato il codice in modo da renderlo puro virtuale – Naveen

+0

Mentre concordo sul fatto che 'A' dovrebbe essere astratto, non penso che abbia bisogno (o dovrebbe avere) e operatore == del tutto. Così come espressioni come 'a == b' avranno un bevahiour molto diverso a seconda dell'ordine e dei tipi di' a' e 'b' mentre potrebbero aspettarsi di essere simmetrici. 'operator ==' ha senso solo per i tipi con semantica del valore. –

2

Le diverse classi derivate possono creare oggetti uguali?

Se è così: double dispatch è un'opzione: non hanno bisogno di sovraccaricare nella classe base, in modo da avere le dipendenze

In caso contrario: una soluzione è nel Operatore ==() per controllare il typeid e restituire false se sono diversi Altrimenti chiama una funzione private equal() in cui la classe derivata può fare un static_cast e confrontare.

bool base::operator==(const base& other) const 
{ 
    if (typeid(*this) != typeid(other)) return false; 
    return equal(other); 
} 

bool derived::equal(const base& other) const 
{ 
    derived& derOther = static_cast<derived&>(other); 
    // compare derOther with *this 
    return true; // there is nothing to compare 
} 

questo modo si evita di tipo controlli in tutte le classi derivate

+0

Ciò impedisce di utilizzare una classe derivata di B (ad esempio, B2) rispetto a B. (Utilizzando la gerarchia dalla domanda.) L'operatore –

+0

==() è definito solo nella classe base, quindi è possibile utilizzare la gerarchia. La funzione equal() deve essere privata (come detto) e solo chiamata dall'operatore ==() – stefaanv

0

Se non si cura di confronto di tipo A a tipo B, o B a C, ecc allora si può semplicemente implementare una parità sovraccarico operatore per ogni classe:

class A { 
    public: int data; 

    bool operator==(const A& rhs) { 
     return (data == rhs.data); 
    } 
}; 
class B : public A { 
    public: float more_data; bool something_else; 

    bool operator==(const B& rhs) { 
     return (A::operator==(data) && 
       more_data == rhs.more_data && 
       something_else == rhs.something_else); 
    } 
}; 

Questo è pericoloso, però, perché se si deriva una nuova classe D da B o C, si sta andando ad avere problemi.

Altrimenti è necessario implementare alcuni comparatori con molto dynamic_cast <> -ing per fare davvero bene. In alternativa è possibile implementare una funzione per creare un codice hash per ciascun oggetto e sfruttare ciò, ad es.

class A { 
    public: int data; 

    virtual long getHashCode() const { 
     // compute something here for object type A 
    } 

    // virtual here just in case you need to overload it in B or C 
    virtual bool equals(const A& obj) const { 
     return (typeid(*this) == typeid(obj) && 
       getHashCode() == obj->getHashCode()); 
    } 
}; 

class B : public A { 
    public: float more_data; bool something_else; 

    virtual long getHashCode() const { 
     // compute something here for object type B 
    } 
}; 

class C : public A { 
    public: double more_data; 

    virtual long getHashCode() const { 
     // compute something here for object type C 
    } 
}; 

Se si incorporano il tipo di oggetto nel codice hash in qualche modo (non mostrato sopra) allora si può anche fare a meno del typeid sciocca() il confronto di cui sopra.

0

Se non ti dispiace la classe di base facendo riferimento alle sottoclassi quindi fare doppio la spedizione:

#include <iostream> 

class B; 
class C; 

class A 
{ 
public: 
    int data; 

    virtual bool equals (const A* rhs) const 
    { 
     std::cout << " A==A "; 
     return data == rhs->data; 
    } 

    virtual bool equals (const B* rhs) const {return false;} 
    virtual bool equals (const C* rhs) const {return false;} 
}; 

class B : public A 
{ 
public: 
    float some_data; 

    virtual bool equals (const A* rhs) const 
    { 
     return rhs->equals (this); 
    } 

    virtual bool equals (const B* rhs) const 
    { 
     std::cout << " B==B "; 
     return A::equals (static_cast<const A*> (rhs)) && some_data == rhs->some_data; 
    } 
}; 

class C : public A 
{ 
public: 
    double more_data; 

    virtual bool equals (const A* rhs) const 
    { 
     return rhs->equals (this); 
    } 

    virtual bool equals (const C* rhs) const 
    { 
     std::cout << " C==C "; 
     return A::equals (static_cast<const A*> (rhs)) && more_data == rhs->more_data; 
    } 
}; 

bool operator== (const A& lhs, const A& rhs) 
{ 
    return lhs.equals (&rhs); 
} 

int main (int argc, char* argv[]) 
{ 

    A* one = new B; 
    A* two = new B; 
    A* three = new C; 

    std::cout << (*one == *one) << std::endl; 
    std::cout << (*one == *two) << std::endl; 
    std::cout << (*one == *three) << std::endl; 
    std::cout << (*three == *three) << std::endl; 

    return 0; 
} 

lo fa senza richiedere dynamic_casts.

Problemi correlati