سؤال

أنا أكتب مكتبة ، وفيها لدي فئة. أي شخص يستخدم المكتبة سيقوم بإنشاء فصوله الخاصة التي ترث من BaseClass. لدي فصل آخر ، دعنا ندعو مدير تكنولوجيا المعلومات ، الذي يحمل متجهًا من مؤشرات BaseClass ، والتي يمكن أن تحتفظ بأي كائن مشتق من BaseClass.

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

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

//Manager.h
#include <vector>
#include "BaseClass.h"
#include <typeinfo>

class Manager {
    //Vector holding pointers to any objects inherited from BaseClass
    vector<BaseClass*> baseClasses;

    //Template function that needs to add NEW object to baseClass vector
    //This I'm having problems with
    template<class T>
    BaseClass* Add() {
        BaseClass* baseClass = new T();
        baseClasses.push_back(baseClass);
        return baseClass;
    }

    //Template function that gets object from baseClass vector
    //This works fine
    template<class T>
    BaseClass* Get() {
        for (int i = 0; i < baseClasses.size(); i++) {
            if (typeid(*baseClasses[i]) == typeid(T)) {
                return baseClasses[i];
            }
        }
        return NULL;
    }
};

على سبيل المثال ، يجب على المستخدم القيام بذلك عند الإضافة إلى كائن أو الحصول على كائن من متجه المدير. DerivedClass مشتق من BaseClass

Manager manager;
//Add a new DerivedClass object to Manager's vector
manager.Add<DerivedClass>();
//Get a pointer to the DerivedClass object that was just added
DerivedClass* derivedClass = (DerivedClass*)manager.Get<DerivedClass>();

وظيفة GET () تعمل بشكل جيد. ما أحتاج إلى معرفته هو ، كيف يمكنني الحصول على وظيفة الإضافة () للعمل؟ سيكون موضع تقدير كبير.

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

المحلول

هناك عدد غير قليل من الأشياء غير واضحة حول تصميمك ، ولكن إذا كان السؤال هو ما إذا كان يمكنك فرض هذا النوع T في وظيفة العضو Templa Add يمكن فرضها على الاشتقاق من BaseClass, ، الخيارات بسيطة:

  • لا تفعل شيئًا ، سيشتكي المترجم بكل سرور على الخط BaseClass* baseClass = new T();
  • أضف تأكيدًا ثابتًا لجعل هذا أكثر وضوحًا
  • استخدم حيل Sfinae الفاخرة لإزالة الوظيفة من مجموعة الأحمال الزائدة

سأذهب لأي من أول اثنين. يمكن تهجئة التأكيد الثابت على النحو التالي:

static_assert(std::is_base_of<BaseClass,T>::value);

خدعة sfinae ، كنت أتجنب ذلك حقًا ، لأنها ستجعل الكود أكثر إرباكًا ، ولكن يمكن تنفيذها على النحو التالي:

template <class T>
typename std::enable_if<std::is_base_of<BaseClass,T>::value,T*>::type
Add() {
   baseClasses.push_back(new T());
   return baseClasses.back();
}

(لاحظ أنني غيرت نوع الإرجاع ليكون T*, ، الكائن هو أ T, ، لماذا تعود أ BaseClass*؟ الأمر نفسه ينطبق على Get وظيفة لا جدوى من إرجاع أ BaseClass*, ، عندما تعلم أن الكائن هو حقا T)

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

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

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

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