سؤال

عند مقارنة كائنين (من نفس النوع)، فمن المنطقي أن يكون لها وظيفة مقارنة التي تأخذ مثيل آخر من نفس الفئة. إذا كنت تنفيذ هذه بوصفها وظيفة الظاهري في الفئة الأساسية، ثم التوقيع على وظيفة لديه مرجع الفئة الأساسية في الفئات المشتقة أيضا. ما هي طريقة أنيقة لمعالجة هذا؟ ينبغي أن قارن لا يكون الظاهري؟

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 وودلالات مقارنة (). المقارنة غير مفهوما مجردا ليس لديها بالضرورة المعنى الصحيح واحد (أو أي معنى على الإطلاق، لهذه المسألة). لا توجد إجابة صحيحة واحدة على هذا السؤال.

وهنا سيناريوهين حيث مقارنة يعني شيئين مختلفين تماما مع نفس التسلسل الهرمي فئة:

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

ويمكن أن نعتبر (على الأقل) طريقتان لالمقارنة بين اثنين من الحمر الوحشية: وهو كبار السن، والتي لديها مزيد من الحجم؟ كلا مقارنات صحيحة والحسابية بسهولة. والفرق هو أن نتمكن من استخدام حجم لمقارنة زيبرا مع أي كائن آخر، ولكن لا يسعنا إلا أن استخدام سن مقارنة الحمر الوحشية مع الحيوانات الأخرى. إذا أردنا مقارنة () لتنفيذ دلالات المقارنة العمر، وأنها لا تقدم أي معنى لتحديد مقارنة () في فئة كائن، لأنه لا يتم تحديد دلالات على هذا المستوى من التسلسل الهرمي. ومن الجدير بالذكر أن أيا من هذه السيناريوهات يتطلب أي الصب، أيا كانت، كما يتم تحديد دلالات على مستوى الفئة الأساسية (سواء كان ذلك الكائن عند مقارنة حجم، أو الحيوان عند مقارنة العمر).

وهذا يثير مسألة أكثر أهمية - أن بعض الفئات لا تتناسب مع وظيفة مقارنة () على كل اللحاق واحد. في كثير من الأحيان أكثر منطقية لتنفيذ المهام المتعددة التي تنص صراحة على ما يجري مقارنة، مثل compare_age () وcompare_volume (). يمكن أن يحدث تعريف هذه الوظائف عند نقطة في التسلسل الهرمي الميراث حيث تصبح دلالات ذات الصلة، ويجب أن تكون تافهة لتكييفها مع الطبقات الطفل (إذا كانت هناك حاجة يتبعون على الإطلاق). مقارنة بسيطة باستخدام مقارنة () أو المشغل == () غالبا ما يكون له معنى مع فئات بسيطة فقط حيث تنفيذ الدلالي الصحيح واضح لا لبس فيه.

وقصة قصيرة طويلة ... "الامر يتوقف".

نصائح أخرى

وأود أن تنفيذ مثل هذا:

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 هو ألف وb هو B) قد ترجع المساواة، وسوف <م> لا بطرح استثناء، في حين إرادة b.Compare(a). أحيانا هذا هو ما تريد، وأحيانا كنه ليس كذلك. إذا لم تكن كذلك، ثم ربما كنت لا تريد وظيفة Compare الخاص بك لتكون الظاهري، أو كنت ترغب في مقارنة type_infos في وظيفة قاعدة 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 متجاوزة مع 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 */
    }
  }
};

وA مقارنة يجب أن يكون معبرا، لذلك:

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 ربما يحتوي على الحقول التي لايوجد مما يعني أن 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 ++. على عكس جافا، نحن لا يشترط أن ترث جميع لدينا فئات من نفس الفئة كائن الجذر. عند التعامل مع (/ قيمة معاني الكلمات) فئات قابلة للمقارنة، فإنه من غير المحتمل جدا أن يكون منهم يأتون من التسلسل الهرمي متعدد الأشكال.

وإذا كانت الحاجة حقيقية في موقفك، وأنت مرة أخرى إلى مزدوج إرسال / مشكلة multimethods. هناك طرق مختلفة لحلها (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