2008-11-16 49 views
9

Khi so sánh hai đối tượng (cùng loại), có nghĩa là có hàm so sánh nhận một thể hiện khác của cùng một lớp. Nếu tôi thực hiện điều này như một hàm ảo trong lớp cơ sở, thì chữ ký của hàm phải tham chiếu lớp cơ sở trong các lớp dẫn xuất cũng có. Cách thanh lịch để giải quyết vấn đề này là gì? Nếu so sánh không được ảo?So sánh đối tượng thanh lịch

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

Không, bạn nên sử dụng toán tử ==. Đó là những gì nó cho. Không cần có chức năng So sánh. – jalf

+0

@ jalf Tôi nghĩ anh ấy muốn làm hơn, bằng, hoặc ít hơn so sánh, như strcmp(). Lưu ý rằng Compare() trả về một int, không phải là một bool. –

Trả lời

1

Nó phụ thuộc vào ngữ nghĩa dự định của A, B, và C và ngữ nghĩa của so sánh(). So sánh là một khái niệm trừu tượng không nhất thiết phải có một ý nghĩa chính xác (hoặc bất kỳ ý nghĩa gì cả, cho vấn đề đó). Không có câu trả lời đúng cho câu hỏi này.

Dưới đây là hai kịch bản mà so sánh có nghĩa là hai điều hoàn toàn khác nhau với hệ thống phân cấp cùng lớp:

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

Chúng ta có thể xem xét (ít nhất) hai cách so sánh hai Zebras: đó là cũ, và trong đó có khối lượng hơn ? Cả hai so sánh đều hợp lệ và dễ tính toán; sự khác biệt là chúng ta có thể sử dụng âm lượng để so sánh Zebra với bất kỳ đối tượng nào khác, nhưng chúng ta chỉ có thể sử dụng tuổi để so sánh Zebras với các động vật khác. Nếu chúng ta muốn so sánh() để thực hiện ngữ nghĩa so sánh tuổi, nó không có ý nghĩa gì khi định nghĩa compare() trong lớp Object, vì ngữ nghĩa không được định nghĩa ở cấp độ phân cấp này. Cần lưu ý rằng không phải trường hợp nào cũng yêu cầu bất kỳ phép đúc nào, vì ngữ nghĩa được định nghĩa ở cấp độ cơ sở (cho dù đó là Đối tượng khi so sánh khối lượng hoặc Động vật khi so sánh tuổi).

Điều này đặt ra vấn đề quan trọng hơn - rằng một số lớp không phù hợp với một hàm so sánh tất cả(). Thông thường, nó có ý nghĩa hơn khi triển khai nhiều hàm để khai báo rõ ràng những gì đang được so sánh, như compare_age() và compare_volume(). Định nghĩa của các hàm này có thể xảy ra tại điểm trong hệ thống phân cấp thừa kế, nơi ngữ nghĩa trở nên có liên quan, và nó không đáng kể để thích nghi chúng với các lớp con (nếu nhu cầu thích ứng). So sánh đơn giản bằng cách sử dụng compare() hoặc operator ==() thường chỉ có ý nghĩa với các lớp đơn giản, nơi việc thực hiện ngữ nghĩa chính xác là rõ ràng và rõ ràng.

Câu chuyện dài ngắn ... "nó phụ thuộc".

0

Nếu bạn có nghĩa là so sánh() trong lớp B hoặc C nên luôn luôn được thông qua một đối tượng của lớp B hoặc C, không có vấn đề gì chữ ký nói, bạn có thể làm việc với các con trỏ tới trường thay vì trường hợp, và cố gắng nhìn xuống con trỏ trong mã của phương pháp sử dụng một cái gì đó giống như

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

(một quá tải như vậy sẽ là cần thiết chỉ dành cho những lớp học có nguồn gốc mà thêm vào tình trạng một cái gì đó cha mẹ của họ mà ảnh hưởng đến việc so sánh.)

0

So sánh phải phản chiếu, vì vậy:

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

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

Vì vậy, các a.equals(b) nên trả về false, vì B có thể chứa các lĩnh vực mà A không có nghĩa là b.equals(a) lẽ sẽ là sai lầm. Vì vậy, trong C++ việc so sánh phải là ảo mà tôi đoán, và bạn nên sử dụng kiểu kiểm tra để xem tham số có thuộc kiểu "giống nhau" với đối tượng hiện tại hay không.

+0

So sánh, không bằng. nghĩ strcmp(). –

0

Ngoài dynamic_cast, bạn cũng cần chuyển tham chiếu hoặc con trỏ, có thể là const. Hàm So sánh cũng có thể là 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; 
    } 
} 

EDIT: Phải biên dịch trước khi gửi bài ...

+0

cảnh báo: trong B :: So sánh, Khác là một đối tượng, do đó mã của bạn sẽ cố gắng chuyển đổi một đối tượng thành con trỏ. Hơn nữa, số không trả về sẽ có nghĩa là bình đẳng; Tôi sẽ tăng một số ngoại lệ thay vì –

+0

bạn sẽ tăng một số ngoại lệ trong trường hợp này? Cái đó ghê thật. – coppro

+0

Cảm ơn. Mã sẽ được biên dịch ngay bây giờ. –

0

Tôi hầu như không có vấn đề này trong C++. Không giống như Java, chúng ta không cần phải kế thừa tất cả các lớp của chúng ta từ cùng một lớp đối tượng gốc. Khi giao dịch với các lớp học có thể so sánh (/ giá trị ngữ nghĩa), rất khó có chúng đến từ một hệ thống phân cấp đa hình.

Nếu nhu cầu là có thật trong trường hợp cụ thể của bạn, bạn sẽ trở lại vấn đề hai công văn/đa phương diện. Có rất nhiều cách để giải quyết nó (dynamic_cast, bảng chức năng cho sự tương tác có thể, du khách, ...)

1

tôi sẽ thực hiện nó như thế này:

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

này rất giống với mô hình IComparable trong .NET, hoạt động rất tốt.

EDIT:

Một caveat đến trên là a.Compare(b) (nơi a là một A và b là một B) có thể trở lại bình đẳng, và không bao giờ sẽ ném một ngoại lệ, trong khi b.Compare(a) ý chí. Đôi khi đây là những gì bạn muốn, và đôi khi nó không phải.Nếu không, thì có thể bạn không muốn chức năng Compare của bạn là ảo, hoặc bạn muốn so sánh type_info s trong hàm cơ sở Compare, như trong:

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

Lưu ý rằng có nguồn gốc lớp Compare chức năng don' t cần phải thay đổi, vì họ nên gọi số Compare của lớp cơ sở, nơi sự so sánh type_info sẽ xảy ra. Tuy nhiên, bạn có thể thay thế dynamic_cast trong chức năng Compare bị ghi đè bằng một số static_cast.

+0

Vấn đề là nếu phần B của đối tượng là khác nhau, nhưng phần A là như nhau, nó trả về bình đẳng. – coppro

+0

Bạn hình dung như thế nào? –

+0

@coppro, tôi không đồng ý. Hãy nhớ rằng 0 có nghĩa là bình đẳng. – wimh

1

Có lẽ, tôi muốn làm điều đó như thế này:

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 */ 
    } 
    } 
}; 
+0

Vẫn còn một vấn đề khi bạn trở lại "không bằng nhau" khi các loại khác nhau, bạn sẽ quay trở lại những gì? Bạn nói "không bằng nhau", nhưng lựa chọn của bạn cho "không bằng nhau" là -1 cho lớn hơn hoặc 1 cho ít hơn. Không có ý nghĩa trong trường hợp này. Ngoài ra, giống như "giải pháp của P-daddy, điều này giả định rằng giá trị loại B không thể ghi đè loại A. Hãy xem xét so sánh" tính khốc liệt "của động vật, trong đó loại A là" Động vật "so sánh sức mạnh một mình và loại B là" Predator ", mà (vì lợi ích của đối số) luôn luôn khốc liệt hơn các loài động vật khác, bất kể sức mạnh. Giá trị của B ghi đè A. –

0

tôi sẽ đề nghị để không làm cho nó ảo. Nhược điểm duy nhất là bạn rõ ràng phải nói so sánh nào để sử dụng nếu các lớp không giống nhau. Nhưng bởi vì bạn phải, bạn có thể phát hiện ra một lỗi (tại thời gian biên dịch) mà nếu không có thể gây ra một lỗi runtime ...

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; 
} 
Các vấn đề liên quan