سؤال

سأصيغ هذا في شكل مثال لجعله أكثر وضوحا.

لنفترض أن لدي ناقلًا للحيوانات وأريد الاطلاع على المصفوفة ومعرفة ما إذا كانت العناصر إما كلابًا أم قططًا؟

class Dog: public Animal{/*...*/};
class Cat: public Animal{/*...*/};

int main()
{
vector<Animal*> stuff;
//cramming the dogs and cats in...

for(/*all elements in stuff*/)
//Something to the effect of:  if(stuff[i].getClass()==Dog) {/*do something*/}

}

آمل أن يكون هذا واضحا نوعا ما.أعرف شيئًا عن typeid، لكن ليس لدي حقًا أي كائن Dog لمقارنته به وأود تجنب إنشاء كائن Dog إذا استطعت.

هل هناك طريقة للقيام بذلك؟شكرا لك مقدما.

هل كانت مفيدة؟

المحلول

كما لاحظ آخرون، لا ينبغي عليك استخدام typeid, ، ولا dynamic_cast عامل التشغيل للحصول على النوع الديناميكي لما يشير إليه المؤشر.تم إنشاء الوظائف الافتراضية لتجنب هذا النوع من القبح.

على أي حال هنا هو ما تفعله إذا كنت حقًا تريد القيام بذلك (لاحظ أن إلغاء مرجعية المكرر سيعطيك Animal* .لذلك إذا قمت بذلك **it سوف تحصل على Animal&):

for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
    if(typeid(**it) == typeid(Dog)) {
        // it's a dog
    } else if(typeid(**it) == typeid(Cat)) {
        // it's a cat
    }
}

لاحظ أنه يمكنك تطبيق typeid يقوم عامل التشغيل بكتابة نفسه أيضًا، كما هو موضح أعلاه.لا تحتاج إلى إنشاء كائن لهذا الغرض.لاحظ أيضًا أن طريقة typeid لا تعمل إذا قمت بتمريرها بمؤشر مثل typeid(*it) .استخدامه بهذه الطريقة سيعطيك فقط typeid(Animal*) وهو غير مفيد.

مشابه، dynamic_cast ممكن استخدامه:

for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
    if(Dog * dog = dynamic_cast<Dog*>(*it)) {
        // it's a dog (or inherited from it). use the pointer
    } else if(Cat * cat = dynamic_cast<Cat*>(*it)) {
        // it's a cat (or inherited from it). use the pointer. 
    }
}

لاحظ أنه في كلتا الحالتين، يجب أن يكون نوع الحيوان متعدد الأشكال.وهذا يعني أنه يجب أن يكون لديه أو يرث وظيفة افتراضية واحدة على الأقل.

نصائح أخرى

ويمكنك استخدام dynamic_cast، طالما أن ناقلات يحتوي على مؤشرات الحيوان.

vector <Animal *> stuff;

for(int i=0;i<stuff.size();i++) {
    Dog *pDog = dynamic_cast <Dog *> (stuff[i]);
    if(pDog) {
        // do whatever with the dog
    }

    Cat *pCat = dynamic_cast <Cat *> (stuff[i]);
    if(pCat) {
        // and so on
    }
}

ولكن يجب عليك أن تدرك أن هذا هو عموما ليست أفضل الممارسات. يجب أن تحاول العمل مع تعدد الأشكال، وليس ضدها. وبعبارة أخرى، في محاولة لكتابة دالة Animal الظاهرية التي Dog وتجاوز Cat، والسماح للمترجم الاتصال التلقائى الحق واحد.

و(أيضا، dynamic_cast بطيء نسبيا، لذلك الكثير منهم سوف تعيق الأداء؛ في حين استدعاء دالة الظاهري العامة فقط تعليمة واحدة)

هل أنت متأكد أنك تريد أن تفعل ذلك؟ ما كنت تنوي القيام به هو العكس تماما من تعدد الأشكال، وتعدد الأشكال هو أفضل شيء في البرمجة كائنية التوجه.

ويتحدث فضفاضة: لا تفعل شيئا <م> إذا كنت الحيوان هو الكلب . السماح التسلسل الهرمي الحيوان يعرف ما يجب القيام به عند واحد من أهدافه هو الكلب! :)

إذا كنت حقا في حاجة الى دعمكم مستوى التطبيق لتحديد مقابل غير الكلاب، الكلاب، يجب تجنب استخدام RTTI (dynamic_cast وtypeid)، وجعل تلك المعرفة الصريحة في التسلسل الهرمي صفك.

for (size_t i = 0; i != v.size(); ++i) {
    if (v[i]->isDog()) { v->cleanupPoop(); }
}

وهناك بعض مزايا الأداء طفيفة، ولكن الفائدة الأساسية وتعريض السلوك الضرورية في واجهة صفك للمبرمجين الصيانة. RTTI (كونه محدودا كما هو) لا ينبغي أن يطلب من أجل التسلسل الهرمي فئة للعمل.

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

ويمكنك استخدام المشغل typeid للقيام بذلك، منها مثلا.

if (typeid(stuff[i].getClass())==typeid(Dog))

وهذا لا يمكن أن قبض اذا كان فئة مشتقة من Dog، وإن كان. يمكنك استخدام dynamic_cast لذلك. ومع ذلك، أي استخدام typeid أو dynamic_cast غالبا ما يكون مؤشرا على وجود خلل التصميم. عادة، لا تحتاج إلى معرفة ما هي أنواع مشتقة، وهناك على الارجح أفضل طريقة التي تنطوي على تعدد الأشكال. من الصعب إعطاء المشورة الصحيحة دون مثالا حقيقيا، وإن كان.

تحليل باستخدام الدالات الظاهرية:

وكما يتبين من ردود الآخرين، وذلك باستخدام الدالات الظاهرية وغالبا ما يكون في الواقع كافية بما فيه الكفاية، وهو "C ++" طريقة التفكير. هنا هو مثال باستخدام وظائف افتراضية:

#include<iostream>
#include<vector>
using namespace std;

/////////////

class Animal {
  public:
    virtual void move() { cout << "animal just moved" << endl; }
};
class Dog : public Animal {
  public:
    void move() { cout << "dog just moved" << endl; }
};
class Cat : public Animal {
  public:
    void move() { cout << "cat just moved" << endl; }
};

void doSomethingWithAnimal(Animal *a) {
  a->move();
}

/////////////

int main() {
  vector<Animal*> vec;
  vector<Animal*>::iterator it;

  Animal *a = new Animal;
  Dog *d = new Dog;
  Cat *c = new Cat;

  vec.push_back(a);
  vec.push_back(d);
  vec.push_back(c);

  it = vec.begin();

  while( it != vec.end() ) {
    doSomethingWithAnimal(*it);

    it++;
  }

  return 0;
}

إذا هذا لن يكون كافيا، ثم الآخرين وقد نشرت بالفعل الإجابات التي تستخدم في الواقع منطق مشروطة بدلا من منطق بلمرة.

والجواب المقبول هو الصحيح، ولكن يجب أن نعرف أن هناك خيار آخر، وكذلك التي لم يتم ذكرها. هل يمكن أن يكون دالة الظاهري في فئة الحيوان يسمى "نوع ()" التي يمكن إرجاع صحيح أو سلسلة (أو أي نوع يمكن مقارنتها).

وهكذا على سبيل المثال:

class Animal {
    /*...*/
public:
    virtual std::string type() const { return "animal"; }
};

class Dog: public Animal{
    /*...*/
public:
    virtual std::string type() const { return "dog"; }
};

class Cat: public Animal{
    /*...*/
public:
    virtual std::string type() const { return "cat"; }
};

وبهذه الطريقة يمكنك أن تفعل فقط:

if(array[i]->type() == "dog") { }

وظيفة نوع يمكن أن يعود أي شيء (عدد صحيح فريدة من نوعها لكل نوع مشتق ستعمل أيضا، ولكن سلاسل توضيح ذلك أفضل).

وببساطة خيار آخر.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top