2009-11-19 25 views
10

thể trùng lặp:
What’s the right way to overload operator== for a class hierarchy?Bình đẳng thử nghiệm cho các lớp thừa kế trong C++

Trong C++, làm thế nào các lớp thừa kế có thể ghi đè lên các bài kiểm tra bình đẳng lớp cơ sở trong một cách có ý nghĩa? Ví dụ, nói rằng tôi có một lớp cơ sở A. Lớp B và C lấy được từ A. Bây giờ cho hai con trỏ đến hai đối tượng A, tôi có thể kiểm tra nếu chúng bằng nhau (bao gồm cả dữ liệu lớp con) không?

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ó cách nào làm sạch không? Đặt cược tốt nhất của tôi là gì?

Cảm ơn!

+2

Có thể trùng lặp: http://stackoverflow.com/questions/1691007/whats-the-right-way-to-overload-operator-for-a-class-hierarchy –

+0

Bạn có muốn so sánh 'T == T không ', trong đó' T' có thể là 'A',' B', hoặc 'C' (tốt), hoặc bạn muốn so sánh' A' với 'B' và' A' với 'C' và' B' với 'C' (có vấn đề)? – sbi

+0

Trong ví dụ trên, tôi muốn so sánh một với hai và ba. Họ đều là một con trỏ. – Imbue

Trả lời

12

Tôi nhớ đọc một mô tả ngắn gọn về thành ngữ công cộng-phi ảo/phi công cộng và lợi thế của nó, nhưng không phải ở đâu. This wikibook có mô tả phù hợp.

Đây là cách bạn áp dụng nó vào 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

Một cách để làm điều này là sử dụng virtual operator== mà mất đối tượng lớp cơ sở như tham số để nó hoạt động đúng với các đối tượng có nguồn gốc khác nhau . Tuy nhiên, bạn cần phải thực hiện chức năng này thuần ảo để buộc tất cả các đối tượng có nguồn gốc thực hiện nó. Vì vậy, bạn sẽ không thể khởi tạo lớp cơ sở. Ví dụ:

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

Có một toán tử ảo không thuần túy == trong lớp cơ sở có khả năng rất nguy hiểm vì bạn không có bảo vệ hoặc cảnh báo đối với các lớp dẫn xuất mới quên ghi đè lên và kết thúc tất cả so sánh bằng nhau nếu chỉ các phần cơ sở bằng nhau. –

+0

Có, bạn đã đúng. Chỉnh sửa mã để làm cho nó tinh khiết ảo – Naveen

+0

Trong khi tôi đồng ý rằng 'A' nên trừu tượng, tôi không nghĩ rằng nó cần (hoặc nên có) và nhà điều hành == ở tất cả. Vì nó là các biểu thức như 'a == b' sẽ có nhiều bevahiour khác nhau tùy thuộc vào thứ tự và kiểu' a' và 'b' trong khi chúng có thể mong đợi đối xứng. 'operator ==' chỉ thực sự có ý nghĩa đối với các kiểu có ngữ nghĩa giá trị. –

2

Các lớp dẫn xuất khác nhau có thể tạo ra các đối tượng bằng nhau không?

Nếu vậy: double dispatch là một lựa chọn: nó không cần quá tải trong lớp cơ sở, vì vậy bạn sẽ phải phụ thuộc

Nếu không: một giải pháp là trong == nhà điều hành() để kiểm tra typeid và trả về false nếu chúng khác nhau. Nếu không, hãy gọi hàm private() riêng trong đó lớp dẫn xuất có thể thực hiện một static_cast và so sánh.

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 
} 

Điều này tránh kiểu kiểm tra ở tất cả các lớp thừa kế

+0

Điều này ngăn cản việc sử dụng lớp dẫn xuất B (nói, B2) so với B. (Sử dụng phân cấp từ câu hỏi.) –

+0

toán tử ==() chỉ được định nghĩa trong lớp cơ sở, vì vậy có thể sử dụng phân cấp. Hàm equal() phải là private (như đã đề cập) và chỉ được gọi bởi toán tử ==() – stefaanv

0

Nếu bạn không quan tâm về sự so sánh của loại A đến loại B, hoặc B đến C, vv sau đó bạn chỉ có thể thực hiện một sự bình đẳng quá tải nhà điều hành cho mỗi lớp:

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

Đó là nguy hiểm, bởi vì nếu bạn nhận được lớp D mới từ B hoặc C, bạn sẽ gặp sự cố.

Nếu không, bạn cần triển khai một số trình so sánh với nhiều dynamic_cast <> -ing để thực sự làm điều đó đúng. Ngoài ra, bạn có thể triển khai hàm để tạo mã băm cho từng đối tượng và tận dụng điều đó, ví dụ:

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

Nếu bạn kết hợp loại đối tượng vào mã băm theo cách nào đó (không được hiển thị ở trên) thì bạn cũng có thể phân phối bằng các so sánh typeid ngớ ngẩn() ở trên.

0

Nếu bạn không nhớ các lớp cơ sở đề cập đến các tiểu lớp sau đó nhấp đúp văn:

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

Liệu nó mà không đòi hỏi dynamic_casts.

Các vấn đề liên quan