2009-11-19 2 views
10

가능한 중복 : C++에서
What’s the right way to overload operator== for a class hierarchy?평등 테스트

, 어떻게하면 파생 클래스가 의미있는 방식으로 기본 클래스 평등 시험을 대체 할 수 있습니다?

예를 들어 기본 클래스 A가 있다고 가정 해 봅시다. 클래스 B와 C는 A에서 파생되었습니다. 이제 두 개의 A 객체에 두 개의 포인터가 주어 졌으므로 이들이 동일한 지 (모든 하위 클래스 데이터 포함) 테스트 할 수 있습니까?

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? 

깨끗한 방법이 있습니까? 내 최선의 방법은 뭔가?

감사합니다.

+2

가능한 복제본 : http://stackoverflow.com/questions/1691007/whats-the-right-way-to-overload-operator-for-a-class-hierarchy –

+0

'T == T ','T'는'A','B'' 또는'C' (벌금)이거나,'A'와'B' 그리고'A'와'C'와'B'를 비교하기를 원하십니까? 'C '(의문의 여지)? – sbi

+0

위 예제에서 두 개와 세 개를 비교하고 싶습니다. 그들은 모두 포인터입니다. – Imbue

답변

12

공개 비 가상/비공개 가상 관용구 및 장점에 대한 간결한 설명을 읽었지만 어디서 읽지 않았는지 기억합니다. This wikibook에 대한 설명이 있습니다. 여기

는 연산 ==에 적용하는 방법입니다

이 일의 방법이 다른 파생 개체와 함께 제대로 작동하도록 매개 변수로 기본 클래스 객체를 취하는 virtual operator==을 사용하는 것입니다
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

하나 . 그러나이 함수를 순수 가상으로 만들어 파생 된 모든 객체에 강제로 구현해야합니다. 따라서 기본 클래스를 인스턴스화 할 수 없습니다. 예 :

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

기본 클래스에서 비 순수 공개 가상 연산자 ==를 사용하면 새 파생 클래스에 대한 보호 또는 경고가 없으므로 잠재적으로 매우 위험합니다. 파생 클래스를 무시하는 것을 잊어 버리고 기본 부분 만 동일하면 모두를 비교하게됩니다. –

+0

네, 맞습니다. 코드를 편집하여 순수한 가상으로 만들었습니다. – Naveen

+0

'A '가 추상적이어야한다는 것에 동의하지만, 필자는 그것이 필요하다고 (또는 있어야 함), 연산자 ==를 전혀 생각하지 않습니다. 'a == b'와 같은 표현식은 'a'와 'b'의 순서와 유형에 따라 크게 다를 것이지만 대칭 적이기를 기대할 수 있습니다. 'operator =='는 값 의미론을 가진 타입에 대해서만 의미가 있습니다. –

2

다른 파생 클래스가 동일한 객체를 만들 수 있습니까? double dispatch는 옵션입니다 :

만약 그렇다면 유형 ID를 확인하고 false를 반환 할 수있는 솔루션이 연산자 ==에() : 당신이 종속

그렇지 않으면이있을 것이다, 그래서 그것이 기본 클래스에 과부하가 필요합니까 그들이 다르다면. 그렇지 않으면 파생 클래스가 static_cast를 수행하고 비교할 수있는 private equal() 함수를 호출합니다. 당신이 등 C에 B 또는 B 형에 대한 A 형의 비교에 대해 걱정하지 않는 경우

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 
} 

이는 단순히 오버로드 평등을 구현할 수있는 모든 파생 클래스

+0

이렇게하면 파생 클래스 B (예 : B2)를 B와 비교하지 못하게됩니다. (질문의 계층 구조 사용) –

+0

연산자 ==()는 기본 클래스에서만 정의되므로 계층을 사용할 수 있습니다. equal() 함수는 private (언급 된대로)이어야하며 operator ==()에 의해서만 호출되어야합니다. – stefaanv

0

를 입력-검사를 피할 수 각 클래스의 연산자 :

B 또는 C에서 새 클래스 D를 파생 시키면 위험 할 수 있으므로 위험합니다. 문제가 발생할 수 있습니다.

그렇지 않으면 실제로 올바르게 수행 할 수있는 많은 dynamic_cast <-이있는 몇 가지 비교기를 구현해야합니다. 또는 각 객체에 대한 해시 코드를 생성하고이를 활용하는 함수를 구현할 수 있습니다 (예 :

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

당신이 어떤 방식 (안 위)의 해시 코드로 객체의 유형을 통합 할 경우

는 또한 위의() 바보 같은 유형 ID와 비교를 분배 할 수 있습니다.

0

당신이 기본 클래스는 다음 하위 클래스에 두 번 파견을 참조 괜찮다면 :

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

이 dynamic_casts을 요구하지 않고 그것을 않습니다.