كيف يمكنني تجنب ألماسة الموت عند استخدام الميراث المتعدد؟

StackOverflow https://stackoverflow.com/questions/137282

  •  02-07-2019
  •  | 
  •  

سؤال

http://en.wikipedia.org/wiki/Diamond_problem

أعرف معناها، لكن ما هي الخطوات التي يمكنني اتخاذها لتجنبها؟

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

المحلول

مثال عملي:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

لاحظ كيف ترث الفئة D من كل من B وC.لكن كلا من B و C يرثان من A.سيؤدي ذلك إلى تضمين نسختين من الفئة A في الجدول vtable.

لحل هذه المشكلة، نحن بحاجة إلى الميراث الظاهري.إنها الفئة (أ) التي يجب أن تكون موروثة فعليًا.لذلك، هذا سوف يحل المشكلة:

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

نصائح أخرى

الميراث الظاهري.هذا ما هو موجود من أجله.

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

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

في هذه الحالات، من السهل مواجهة مشكلات الميراث المتعددة.

لقد قمت بحلها باستخدام التكوين والمؤشرات التي تعود إلى المالك:

قبل:

class Employee : public WidgetListener, public LectureAttendee
{
public:
     Employee(int x, int y)
         WidgetListener(x), LectureAttendee(y)
     {}
};

بعد:

class Employee
{
public:
     Employee(int x, int y)
         : listener(this, x), attendee(this, y)
     {}

     WidgetListener listener;
     LectureAttendee attendee;
};

نعم، حقوق الوصول مختلفة، ولكن إذا تمكنت من الإفلات من هذا النهج، دون تكرار التعليمات البرمجية، فهذا أفضل لأنه أقل قوة.(يمكنك توفير الطاقة عندما لا يكون لديك بديل.)

class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

في هذا تتكرر سمات الفئة أ مرتين في الفئة د مما يزيد من استخدام الذاكرة ...لذلك، لتوفير الذاكرة، نقوم بإنشاء سمة افتراضية لجميع السمات الموروثة من الفئة A والتي يتم تخزينها في Vtable.

حسنًا، إن الشيء العظيم في الماسة اللعينة هو أنه يحدث خطأ عند حدوثها.أفضل طريقة لتجنب ذلك هي معرفة هيكل الميراث الخاص بك مسبقًا.على سبيل المثال، أحد المشاريع التي أعمل عليها يحتوي على عارضين ومحررين.المحررون عبارة عن فئات فرعية منطقية للعارضين، ولكن نظرًا لأن جميع العارضين عبارة عن فئات فرعية - TextViewer وImageViewer وما إلى ذلك، فإن المحرر لا يشتق من Viewer، وبالتالي يسمح لفئات TextEditor وImageEditor النهائية بتجنب الماس.

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

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

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

إذا لم يكن الأمر كذلك، فاستخدم الوظائف/الواجهات الافتراضية.

استخدام الميراث عن طريق التفويض.بعد ذلك، ستشير كلا الفئتين إلى القاعدة A، لكن يتعين عليهما تنفيذ أساليب تعيد التوجيه إلى القاعدة A.له تأثير جانبي يتمثل في تحويل الأعضاء المحميين من A إلى أعضاء "خاصين" في B وC وD، لكنك الآن لا تحتاج إلى أعضاء افتراضيين، وليس لديك ألماس.

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