سؤال

أنا حاليا يعاني هفوة.لقد فعلت هذا من قبل ولكن لا أذكر بالضبط الجملة و أنا لا يمكن أن ننظر إلى رمز كتبت لأني كنت أعمل في شركة أخرى في ذلك الوقت.علي هذا الترتيب:

class P
{
// stuff
};

class PW : public P
{
// more stuff
};

class PR : public P
{
// more stuff
};

class C
{
public:
    P GetP() const { return p; }    
private:
    P p;
};

// ...
    P p = c.GetP( ); // valid
    PW p = c.GetP( ); // invalid
    PR p = c.GetP( ); // invalid
// ...

الآن أود أن جعل P للتبادل مع PW و العلاقات العامة (وبالتالي PW و العلاقات العامة يمكن أن يكون متبادل).أنا يمكن أن تحصل على الأرجح بعيدا مع يلقي ولكن هذا رمز التغيير قد حدث مرات قليلة جدا في هذه الوحدة وحدها.أنا متأكد من أنه هو المشغل ولكن بالنسبة لي لا أستطيع أن أتذكر ما.

كيف يمكنني جعل P للتبادل مع PW والعلاقات العامة مع الحد الأدنى من الكود ؟

تحديث: لإعطاء المزيد من التوضيحات.P تقف على المشروع و R و W يقف القارئ والكاتب على التوالي.كل قارئ هو رمز التحميل - لا والمتغيرات الكاتب مدونة لمجرد الكتابة.فإنه يحتاج إلى أن تكون منفصلة لأن القراءة والكتابة أقسام مختلفة المدير دروس و الحوارات التي ليس من مشاريع القلق الحقيقي الذي هو التلاعب في ملفات المشروع.

تحديث: أنا أيضا بحاجة إلى أن تكون قادرة على استدعاء أساليب P و PW.حتى إذا كان P لديه الأسلوب() و PW كوسيلة الاتصال ب() ثم أنا يمكن أن :

PW p = c.GetP();
p.a();
p.b();

انها في الأساس إلى إجراء عملية التحويل شفافة.

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

المحلول

إذا كنت ترغب في الحصول على هذا الجزء ترجمة:



// ...
    P p = c.GetP( ); // valid
    PW p = c.GetP( ); // invalid
    PR p = c.GetP( ); // invalid
// ...

تحتاج إلى أن تكون قادرة على بناء/تحويل الصفحة إلى PW أو العلاقات العامة.تحتاج أن تفعل شيئا مثل هذا:



class PW : public P
{
    PW(const P &);
// more stuff
};

class PR : public P
{
    PR(const P &);
// more stuff
};

أو هل يعني شيئا أكثر من ذلك مثل:


class P
{
    operator PW() const;
    operator PR() const;
// stuff
};

نصائح أخرى

أنت تحاول إجبار الفعلية المتغيرات بدلا من المؤشرات.للقيام بذلك يتطلب المدلى بها.ومع ذلك ، إذا كان تعريف الفئة يشبه هذا:

class C

    {
        public: 
            P* GetP() const { return p; }
        private:
            P* p;
    }

ثم إذا كان p كان مؤشر إلى P, PW, أو العلاقات العامة وظيفة الخاص بك لن تتغير ، أي (الظاهري) وظائف دعا P* إرجاعها بواسطة الدالة استخدام التنفيذ من P, PW أو العلاقات العامة اعتمادا على ما عضو p كان..

أعتقد أن الشيء الرئيسي أن نتذكر هو Liskov مبدأ الإحلال.منذ PW والعلاقات العامة والفئات الفرعية من P, أنها يمكن أن تعامل كما لو كانت Ps.ومع ذلك ، PWs لا يمكن أن تعامل على أنها استراتيجية الحد من الفقر ، والعكس بالعكس.

في الكود أعلاه لديك عكس تقطيع المشكلة.

ما تحاول القيام به هو تعيين من P إلى PW أو العلاقات العامة التي تحتوي على معلومات أكثر من الكائن المصدر.كيف يمكنك أن تفعل هذا ؟ أقول P فقط الأعضاء 3 متغيرات ، ولكن PW 12 أعضاء إضافيين - أين قيم هذه تأتي عندما تكتب PW p = c.GetP()?

إذا كان هذا الواجب في الواقع هو صالحة أن تشير إلى نوع من التصميم غرابة ، ثم أود أن تنفيذ PW::operator=(const P&) و PR::operator=(const P&), PW::PW(const P&) و PR::PR(const P&).ولكن لم أكن النوم جيدا تلك الليلة.

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

class P
{
public:
    template <typename T>
    operator T() const
    {
        T t;
        static_cast<T&>(t) = *this;
        return t;
    }
};

لجعل PW والعلاقات العامة للاستخدام عبر P كنت بحاجة إلى استخدام مراجع (أو المؤشرات).لذلك كنت حقا بحاجة t تغيير واجهة ج حتى يعود إشارة.

المشكلة الرئيسية في القانون القديم كنت نسخ P في PW أو العلاقات العامة.ليس هذا هو الذهاب إلى العمل كما PW والعلاقات العامة يحتمل أن يكون على مزيد من المعلومات من P و من نوع منظور كائن من نوع P هو PW أو العلاقات العامة.على الرغم من PW والعلاقات العامة على حد سواء P.

تغيير التعليمات البرمجية إلى هذا وسوف ترجمة:إذا كنت ترغب في العودة كائنات مختلفة مشتقة من P class في وقت التشغيل ثم فئة C يجب أن تكون قادرة على تخزين جميع أنواع مختلفة عليك أن تكون متخصصة في وقت التشغيل.وذلك في فئة أدناه تسمح لك أن تتخصص عن طريق تمرير مؤشر إلى كائن التي يتم إرجاعها من قبل المرجعية.للتأكد من أن الكائن هو استثناء آمنة علي اختتم مؤشر في مؤشر ذكية.

class C
{
    public:
        C(std::auto_ptr<P> x):
            p(x)
        {
            if (p.get() == NULL) {throw BadInit;}
        }
        // Return a reference.
        P& GetP() const { return *p; }        
    private:
        // I use auto_ptr just as an example
        // there are many different valid ways to do this.
        // Once the object is correctly initialized p is always valid.
        std::auto_ptr<P> p;
};

// ...
P&  p = c.GetP( );                   // valid
PW& p = dynamic_cast<PW>(c.GetP( )); // valid  Throws exception if not PW
PR& p = dynamic_cast<PR>(c.GetP( )); // valid  Thorws exception if not PR
// ...

ربما تقصد dynamic_cast المشغل ؟

أنها ليست تماما للتبادل.PW هو P.العلاقات العامة أ. بولكن ف ليس بالضرورة PW, وليس بالضرورة العلاقات العامة.يمكنك استخدام static_cast أن يلقي مؤشرات من PW * ف * أو من العلاقات العامة * ف *.يجب عدم استخدام static_cast أن يلقي الكائنات الفعلية إلى سوبر الدرجة بسبب "تشريح".E.ز.إذا كنت الزهر كائن من PW ف الاشياء الاضافية في PW سيكون "شرائح" قبالة.يمكنك أيضا استخدام static_cast أن يلقي من ف * إلى PW *.إذا كان لديك حقا أن تفعل ذلك ، استخدام dynamic_cast التي سوف تحقق في وقت التشغيل ما إذا كان الكائن هو في الواقع حق sub-class, و تعطيك خطأ وقت التشغيل إذا لم يكن.

أنا لا أفهم ما تقصد بالضبط لكن تتحمل معي.

إنهم بالفعل.فقط ندعو كل شيء P و يمكنك التظاهر العلاقات العامة و PW هي P.العلاقات العامة و PW لا تزال مختلفة على الرغم من.

مما يجعل كل ثلاثة يعادل سيؤدي إلى مشاكل مع Liskov مبدأ.ولكن لماذا كنت تعطي لهم أسماء مختلفة إذا كانوا حقا ما يعادل?

الثانية والثالثة ستكون باطلة لأنها ضمني upcast -- وهو شيء خطير في C++.وذلك لأن الدرجة التي أنت الصب أن لديها المزيد من الوظائف من الفئة يتم تعيين إلا إذا كنت صراحة يلقي ذلك بنفسك, برنامج التحويل البرمجي C++ سوف رمي خطأ (على الأقل يجب أن).بالطبع, هذا هو تبسيط الأمور قليلا (يمكنك استخدام RTTI عن بعض الأمور التي قد تتصل ما تريد القيام به بأمان دون الاحتجاج غضب سيئة كائن'ness) -- ولكن البساطة هي دائما وسيلة جيدة نهج المشاكل.

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

استخدام المرجعي أو المؤشر P بدلا من كائن:

class C
{
 public:
  P* GetP() const { return p; }
 private:
  P* p;
};

هذا سوف يسمح PW* أو العلاقات العامة* أن تكون ملزمة C. p.ومع ذلك ، إذا كنت بحاجة إلى الذهاب من P إلى PW أو العلاقات العامة ، تحتاج إلى استخدام dynamic_cast<PW*>(p) الذي سيعود PW* نسخة من ف ، أو NULL ف ليس PW* (فعلى سبيل المثال, لأنها العلاقات العامة*).Dynamic_cast بعض النفقات العامة ، على الرغم من أنه من الأفضل تجنبها إذا كان ذلك ممكنا (استخدام virtuals).

يمكنك أيضا استخدام typeid() المشغل تحديد وقت تشغيل نوع كائن ، ولكن لديها العديد من القضايا ، بما في ذلك التي يجب أن تشمل وأنه لا يمكن الكشف عن إضافي الاشتقاق.

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