문제

동일한 유형의 두 객체를 비교할 때 동일한 클래스의 다른 인스턴스를 취하는 비교 함수를 갖는 것이 좋습니다. 기본 클래스에서이를 가상 함수로 구현하면 기능의 서명은 파생 클래스에서도 기본 클래스를 참조해야합니다. 이것을 다루는 우아한 방법은 무엇입니까? 비교가 가상이어야합니까?

class A
{
    A();
    ~A();
    virtual int Compare(A Other);
}

class B: A
{
    B();
    ~B();
    int Compare(A Other);
}

class C: A
{
    C();
    ~C();
    int Compare(A Other);
}
도움이 되었습니까?

해결책

A, B 및 C의 의도 된 의미와 Compare ()의 의미론에 따라 다릅니다. 비교는 반드시 하나의 정확한 의미 (또는 그 문제에 대해 어떤 의미도)를 갖지는 않는 추상 개념입니다. 이 질문에 대한 단일 정답은 없습니다.

동일한 클래스 계층 구조를 가진 두 가지 완전히 다른 두 가지를 비교하는 두 가지 시나리오는 다음과 같습니다.

class Object 
{
    virtual int compare(const Object& ) = 0;
    float volume;
};

class Animal : Object 
{
    virtual int compare(const Object& );
    float age;
};

class Zebra  : Animal 
{
    int compare(const Object& );
};

우리는 두 개의 얼룩말을 비교하는 두 가지 방법을 고려할 수 있습니다. 나이가 많고 볼륨이 더 많습니까? 두 비교는 유효하고 쉽게 계산할 수 있습니다. 차이점은 양을 사용하여 얼룩말을 다른 물체와 비교할 수 있지만 나이를 사용하여 얼룩말을 다른 동물과 비교할 수 있다는 것입니다. 비교 ()가 연령 비교 의미론은 기본 클래스 수준 (부피를 비교할 때의 대상이든, 나이를 비교할 때 동물이든)에서 정의되므로 이러한 시나리오 중 어느 것도 캐스팅을 요구하지 않는다는 점은 주목할 가치가 있습니다.

이는 일부 클래스가 단일 Catch-All Compare () 함수에 적합하지 않다는 더 중요한 문제를 제기합니다. compare_age () 및 compare_volume ()과 같이 비교중인 내용을 명시 적으로 명시하는 여러 기능을 구현하는 것이 더 합리적입니다. 이러한 기능의 정의는 의미론이 관련이있는 상속 계층의 시점에서 발생할 수 있으며, 아동 계급에 적응하는 것은 사소한 일이어야합니다 (전혀 조정이 필요하다면). Compare () 또는 Operator == ()를 사용한 간단한 비교 종종 올바른 시맨틱 구현이 명백하고 모호하지 않은 간단한 클래스에서만 적합합니다.

짧은 이야기 ... "그것은 의존한다".

다른 팁

나는 다음과 같이 구현할 것입니다.

class A{
    int a;

public:
    virtual int Compare(A *other);
};


class B : A{
    int b;

public:
    /*override*/ int Compare(A *other);
};

int A::Compare(A *other){
    if(!other)
        return 1; /* let's just say that non-null > null */

    if(a > other->a)
        return 1;

    if(a < other->a)
        return -1;

    return 0;
}

int B::Compare(A *other){
    int cmp = A::Compare(other);
    if(cmp)
        return cmp;

    B *b_other = dynamic_cast<B*>(other);
    if(!b_other)
        throw "Must be a B object";

    if(b > b_other->b)
        return 1;

    if(b < b_other->b)
        return -1;

    return 0;
}

이것은와 매우 유사합니다 IComparable .NET의 패턴, 매우 잘 작동합니다.

편집하다:

위의 경고 중 하나는 그 것입니다 a.Compare(b) (어디 a A와 b B) 평등을 반환 할 수 있으며 절대 예외를 던지십시오 b.Compare(a) 할 것이다. 때때로 이것은 당신이 원하는 것이며 때로는 그렇지 않습니다. 그렇지 않다면 아마 당신은 아마 당신의 Compare 가상으로 기능하거나 비교하고 싶습니다. type_info기지에서 s Compare 에서와 같이 기능 :

int A::Compare(A *other){
    if(!other)
        return 1; /* let's just say that non-null > null */

    if(typeid(this) != typeid(other))
        throw "Must be the same type";

    if(a > other->a)
        return 1;

    if(a < other->a)
        return -1;

    return 0;
}

파생 수업 ' Compare 기능은 기본 클래스를 호출해야하므로 변경할 필요가 없습니다. Compare, 어디에 type_info 비교가 발생합니다. 그러나 교체 할 수 있습니다 dynamic_cast 재정의에서 Compare a static_cast.

아마도, 나는 이것을 그렇게 할 것입니다 :

class A
{
 public:
  virtual int Compare (const A& rhs) const
  {
    // do some comparisons
  }
};

class B
{
 public:
  virtual int Compare (const A& rhs) const
  {
    try
    {
      B& b = dynamic_cast<A&>(rhs)
      if (A::Compare(b) == /* equal */)
      {
        // do some comparisons
      }
      else
        return /* not equal */;
    }
    catch (std::bad_cast&)
    {
      return /* non-equal */
    }
  }
};

비교는 반사적이어야하므로 :

let a = new A
let b = new B (inherits from A)

if (a.equals(b))
 then b.equals(a) must be true!

그래서 a.equals(b) B는 아마도 A가없는 필드를 포함하기 때문에 False를 반환해야합니다. b.equals(a) 아마도 거짓 일 것입니다.

따라서 C ++에서 비교는 가상이어야하며, 유형 검사를 사용하여 매개 변수가 현재 객체와 "동일"유형인지 확인해야합니다.

클래스 B 또는 C의 비교 ()가 항상 클래스 B 또는 C의 객체를 전달해야한다는 것을 의미한다면, 서명이 말하면 인스턴스 대신 인스턴스로 포인터로 작업하고 포인터를 다운 캐스트 할 수 있습니다. 방법을 사용하는 방법의 코드

int B::Compare(A *ptr)
{
   other = dynamic_cast <B*> (ptr);
   if(other)
      ...  // Ok, it was a pointer to B
}

(이러한 과부하는 부모의 상태에 비교에 영향을 미치는 무언가를 추가하는 파생 클래스에만 필요합니다.)

C ++ 에서이 문제가 거의 없습니다. Java와 달리 동일한 루트 객체 클래스에서 모든 클래스를 상속받을 필요는 없습니다. 비교할 수있는 (/value semantics) 클래스를 다룰 때, 그것들이 다형성 계층 구조에서 나올 가능성은 거의 없습니다.

특정 상황에서 필요가 실제라면, 당신은 더블 디스패치/멀티 메트로 드 문제로 돌아갑니다. 이를 해결하는 다양한 방법이 있습니다 (Dynamic_cast, 가능한 상호 작용을위한 기능 테이블, 방문자 등).

Dynamic_cast 외에도 참조 또는 포인터, 아마도 const를 전달해야합니다. 비교 함수는 아마도 const 일 수도 있습니다.

class B: public A
{
    B();
    virtual ~B();
    virtual int Compare(const A &Other) const;
};


int B::Compare(const A &Other) const
{
    const B *other = dynamic_cast <const B*> (&Other);
    if(other) {
        // compare
    }
    else {
        return 0;
    }
}

편집 : 게시하기 전에 컴파일해야합니다 ...

나는 그것을 가상으로 만들지 말 것을 제안합니다. 유일한 단점은 클래스가 동일하지 않은 경우 사용과 비교하는 것을 명시해야한다는 것입니다. 그러나해야하기 때문에 오류 (컴파일 시간에)를 발견하여 런타임 오류가 발생할 수 있습니다 ...

class A
{
  public:
    A(){};
    int Compare(A const & Other) {cout << "A::Compare()" << endl; return 0;};
};

class B: public A
{
  public:
    B(){};
    int Compare(B const & Other) {cout << "B::Compare()" << endl; return 0;};
};

class C: public A
{
  public:
    C(){};
    int Compare(C const & Other) {cout << "C::Compare()" << endl; return 0;};
};

int main(int argc, char* argv[])
{
    A a1;
    B b1, b2;
    C c1;

    a1.Compare(b1);     // A::Compare()
    b1.A::Compare(a1);  // A::Compare()
    b1.Compare(b2);     // B::Compare()
    c1.A::Compare(b1);  // A::Compare()

    return 0;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top