sort - vector c++ شرح




C++ One std:: vector يحتوي على فئة قالب لأنواع متعددة (3)

إذا كنت تبحث في حاوية لتخزين أنواع متعددة ، فيجب عليك استكشاف Boost.Variant من مكتبة التحسين الشائعة.

أحتاج إلى تخزين أنواع متعددة لفئة القالب في متجه واحد.

على سبيل المثال ، من أجل:

template <typename T>
class templateClass{
     bool someFunction();
};

أحتاج إلى ناقل واحد يخزن كل ما يلي:

templateClass<int> t1;
templateClass<char> t2;
templateClass<std::string> t3;
etc

بقدر ما أعرف هذا غير ممكن ، إذا كان يمكن للشخص أن يقول كيف؟

إذا لم يكن من الممكن أن يشرح أحدهم كيفية إجراء العمل التالي؟

كمحاولة ، حاولت استخدام فئة أساسية غير القالب وترث فئة القالب منه.

 class templateInterface{
     virtual bool someFunction() = 0;
 };

 template <typename T>
 class templateClass : public templateInterface{
     bool someFunction();
 };

ثم قمت بإنشاء متجه لتخزين الطبقة "templateInterface" قاعدة:

std::vector<templateInterface> v;
templateClass<int> t;
v.push_back(t);

هذا إنتاج الخطأ التالي:

error: cannot allocate an object of abstract type 'templateInterface'
note: because the following virtual functions are pure within 'templateInterface'
note: virtual bool templateInterface::someFunction()

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

على سبيل المثال:

 class templateInterface{
     virtual bool someFunction() {return true;}
 };

 template <typename T>
 class templateClass : public templateInterface{
     bool someFunction() {return false;}
 };

 std::vector<templateInterface> v;
 templateClass<int> i;
 v.push_back(i);
 v[0].someFunction(); //This returns true, and does not use the code in the 'templateClass' function body

هل هناك أي طريقة لإصلاح هذا بحيث يتم استخدام الدالة التي تم تجاوزها ، أو هل هناك حل بديل آخر لتخزين أنواع متعددة من القوالب في متجه واحد؟


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


لماذا لا يعمل الرمز الخاص بك:

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

كيف يمكن اصلاح هذا؟

يمكن تكرار المشكلة نفسها باستخدام مقتطف الشفرة هذا:

templateInterface x = templateClass<int>(); // Type slicing takes place!
x.someFunction();  // -> templateInterface::someFunction() is called!

يعمل تعدد الأشكال فقط على مؤشر أو نوع مرجع . عندئذٍ ، سيستخدم نوع وقت التشغيل للكائن خلف المؤشر / المرجع لتحديد التنفيذ الذي سيتم الاتصال به (باستخدام vtable).

مؤشرات تحويل تماما "آمنة" فيما يتعلق بنوع تقطيع. لن يتم تحويل قيمك الفعلية على الإطلاق وسيعمل تعدد الأشكال كما هو متوقع.

مثال ، مماثل لمقتطف الشفرة أعلاه:

templateInterface *x = new templateClass<int>();  // No type slicing takes place
x->someFunction();  // -> templateClass<int>::someFunction() is called!

delete x;  // Don't forget to destroy your objects.

ماذا عن المتجهات؟

لذلك عليك أن تعتمد هذه التغييرات في التعليمات البرمجية الخاصة بك. يمكنك ببساطة تخزين المؤشرات على الأنواع الفعلية في المتجه ، بدلاً من تخزين القيم مباشرة.

عند التعامل مع المؤشرات ، عليك أيضًا الاهتمام بحذف الكائنات المخصصة الخاصة بك. لهذا ، يمكنك استخدام المؤشرات الذكية التي تهتم بالحذف تلقائيًا. unique_ptr هو أحد أنواع المؤشرات الذكية. فإنه يحذف المستأجر كلما يخرج من نطاق ("ملكية فريدة" - النطاق هو المالك). بافتراض أن عمر كائناتك مرتبط بالنطاق ، فهذا ما يجب عليك استخدامه:

std::vector<std::unique_ptr<templateInterface>> v;

templateClass<int> *i = new templateClass<int>();    // create new object
v.push_back(std::unique_ptr<templateInterface>(i));  // put it in the vector

v.emplace_back(new templateClass<int>());   // "direct" alternative

ثم ، استدعاء دالة ظاهرية على أحد هذه العناصر باستخدام بناء الجملة التالي:

v[0]->someFunction();

تأكد من جعل جميع الوظائف الافتراضية التي يمكن أن يكون من الممكن تجاوزها من قبل الفئات الفرعية. وإلا لن يتم استدعاء الإصدار المتجاوز. ولكن منذ أن قمت بالفعل بتقديم "واجهة" ، أنا متأكد من أنك تعمل مع وظائف مجردة.

النهج البديلة:

الطرق البديلة لفعل ما تريد هو استخدام نوع مختلف في المتجه. هناك بعض التطبيقات من أنواع مختلفة ، و Boost.Variant كونها شعبية جدا. هذا النهج لطيف بشكل خاص إذا لم يكن لديك تسلسل هرمي من نوع (على سبيل المثال عند تخزين أنواع بدائية). يمكنك عندئذ استخدام نوع متجه مثل std::vector<boost::variant<int, char, bool>>







stdvector