سؤال

باستخدام sizeof مشغل يمكنني تحديد حجم أي نوع – ولكن كيف يمكنني حيوي تحديد حجم متعدد الأشكال الفئة في وقت التشغيل?

على سبيل المثال لدي مؤشر إلى Animal, و أريد الحصول على حجم الكائن الفعلي هو الذي ستكون مختلفة لو كان هو Cat أو Dog.هناك طريقة بسيطة للقيام بذلك ، قصيرة من خلق أسلوب الظاهري Animal::size والحمولة الزائدة للعودة إلى sizeof من كل نوع ؟

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

المحلول

إذا كنت تعرف مجموعة من الأنواع الممكنة، يمكنك استخدام RTTI لمعرفة نوع الديناميكي عن طريق القيام dynamic_cast. إذا لم تقم بذلك، فإن السبيل الوحيد هو من خلال وظيفة افتراضية.

نصائح أخرى

وأو يمكنك استخدام typeid، والتي قد تكون أسرع من dynamic_cast (أيضا مع dynamic_cast يمكنك يلقي على أنواع وسيطة في التسلسل الهرمي).

ويبدو سيئا إلى حد ما:

#include <iostream>
#include <typeinfo>

class Creature
{
    char x[4];
public:
    virtual ~Creature() {}
};

class Animal: public Creature { char x[8];};

class Bird: public Creature { char x[16]; };

class Dog: public Animal { char x[32]; };

class Cat: public Animal { char x[64]; };

class Parrot: public Bird { char x[128]; };

unsigned creature_size(const Creature& cr)
{
    if (typeid(cr) == typeid(Animal)) {
        return sizeof (Animal);
    }
    else if (typeid(cr) == typeid(Dog)) {
        return sizeof(Dog);
    }
    else if (typeid(cr) == typeid(Cat)) {
        return sizeof(Cat);
    }
    else if (typeid(cr) == typeid(Bird)) {
        return sizeof(Bird);
    }
    else if (typeid(cr) == typeid(Parrot)) {
        return sizeof(Parrot);
    }
    else if (typeid(cr) == typeid(Creature)){
        return sizeof(Creature);
    }
    assert(false && "creature_size not implemented for this type");
    return 0;
}

int main()
{
    std::cout << creature_size(Creature()) << '\n'
    << creature_size(Animal()) << '\n'
    << creature_size(Bird()) << '\n'
    << creature_size(Dog()) << '\n'
    << creature_size(Cat()) << '\n'
    << creature_size(Parrot()) << '\n' ;
}

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

virtual unsigned size() const { return sizeof(*this); }

ويمكنك ان تجعل مجردة في الفئة الأساسية وهو ما يعني أنه سيكون خطأ المترجم إذا كنت قد نسيت لتجاوز هذا الأسلوب.

وتحرير: هذا هو بطبيعة الحال على افتراض أن إعطاء أي مخلوق كنت تريد أن تعرف حجمها. إذا كان لديك سبب قوي للاعتقاد انك تتعامل مع الكلب - أو فئة فرعية من الكلب (وأنت لا يهمني إذا كان هو فئة فرعية)، ثم بطبيعة الحال يمكنك استخدام dynamic_cast ل<م> مخصص الاختبار.

إذا كنت قادرا على تغيير المصدر دروس تصميم, كنت يمكن أن تحل محل تماما ديناميكية تعدد الأشكال (الذي يستخدم وظائف افتراضية) مع ساكنة تعدد الأشكال واستخدام CRTP لغة:

template <class TDerived>
class Base
{
public:
    int getSize()
    { return sizeof(TDerived); }

    void print()
    {
          std::cout
             << static_cast<TDerived*>(this)->getSize()
             << std::endl;
    }

    int some_data;
};

class Derived : public Base<Derived>
{
public:
    int some_other_data1;
    int some_other_data2;
};

class AnotherDerived : public Base<AnotherDerived>
{
public:
    int getSize()
    { return some_unusual_calculations(); }
    // Note that the static_cast above is required for this override to work,
    //  because we are not using virtual functions
};

int main()
{
    Derived d;
    d.print();

    AnotherDerived ad;
    ad.print();

    return 0;
}

يمكنك القيام بذلك عند الحاجة متعددة الأشكال السلوك البرنامج يمكن تحديدها في وقت التحويل البرمجي (مثل sizeof الحالة) ، منذ CRTP لم المرونة الديناميكية تعدد الأشكال لحل الكائن المطلوب في وقت التشغيل.

ثابت تعدد الأشكال أيضا في الاستفادة من الأداء العالي من خلال إزالة الظاهري-وظيفة-دعوة علوية.

إذا كنت لا تريد أن templatize قاعدة الطبقة أو تحتاج إلى عقد مختلف الحالات المستمدة من قاعدة الطبقة في نفس الموقع (مثل مجموعة أو ناقلات) ، يمكنك استخدام CRTP على الطبقة الوسطى و نقل متعددة الأشكال سلوك تلك الفئة (على غرار متعددة الأشكال نسخ البناء سبيل المثال في ويكيبيديا):

class Base
{
public:
    virtual int getSize() = 0;

    void print()
    {
        std::cout << getSize() << std:endl;
    }

    int some_data;
};

template <class TDerived>
class BaseCRTP: public Base
{
public:
    virtual int getSize()
    { return sizeof(TDerived); }
};

class Derived : public BaseCRTP<Derived>
{
    // As before ...
};

class AnotherDerived : public BaseCRTP<AnotherDerived>
{
    // As before ...

    // Note that although no static_cast is used in print(),
    //  the getSize() override still works due to virtual function.
};

Base* obj_list1[100];
obj_list1[0] = new Derived();
obj_list1[2] = new AnotherDerived();

std::vector<Base*> obj_list2;
obj_list2.push_back(new Derived());
obj_list2.push_back(new AnotherDerived());

--
تحديث: أنا الآن وجدت مماثلة ولكن أكثر تفصيلا الجواب على ستاكوفيرفلوو وهو ما يفسر أنه إذا كنا كذلك تستمد من الفئات المشتقة أعلاه (على سبيل المثال class FurtherDerived : public Derived {...}) ، sizeof لا التقرير بشكل صحيح.وقال انه يعطي أكثر تعقيدا البديل مدونة للتغلب على هذا.

لا أستطيع أن أصدق أن شخصا ما هو اخترع type_id () بدلا من تنفيذ الصفات المناسبة ....

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