سؤال

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

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

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

سيكون موضع تقدير أي تلميحات/اقتراحات بشأن الخيارات الممكنة.

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

المحلول

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

ونلقي نظرة على http://en.wikipedia.org/wiki/Observer_pattern

نصائح أخرى

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

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

أعتقد أنك يجب أن تفكر في استخدامه بالطريقة التي أوصى بها مايك:

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

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

وهو في الأساس هناك فصلا كاملا حول هذا الموضوع في تصميم ++ C الحديثة ألكسندريسكو في (Chaper 6، سنغلتونس). ويحدد فئة المفردة التي يمكن إدارة التبعيات، حتى بين سنغلتونس أنفسهم.

ووللغاية وأوصت الكتاب كله راجع للشغل أيضا.

بضعة اقتراحات:

  • اجعل وحدة التحكم المشرف مفردة (أو قم بتغليفها في كائن مفرد تقوم بإنشائه لهذا الغرض) يتم الوصول إليه عبر طريقة ثابتة تُرجع مؤشرًا، ثم يمكن لـ dtors للكائنات المسجلة استدعاء الوصول الثابت (والذي في حالة التطبيق إيقاف التشغيل وتم تدمير وحدة التحكم المشرف سيعود NULL) ويمكن لهذه الكائنات تجنب استدعاء طريقة إلغاء التسجيل في هذه الحالة.

  • قم بإنشاء وحدة التحكم المشرف على الكومة باستخدام جديد واستخدم شيئًا مثل boost::shared_ptr<> لإدارة عمرها.تسليم shared_ptr<> في طريقة الوصول الثابت للمفردة.

يوفر GNU gcc/g++ سمات غير محمولة للأنواع المفيدة جدًا.واحدة من هذه الصفات هي init_priority يحدد الترتيب الذي يتم به إنشاء الكائنات العالمية، ونتيجة لذلك، الترتيب العكسي الذي يتم تدميرها به.من الرجل:

init_priority (الأولوية)

 In Standard C++, objects defined at namespace scope are guaranteed
 to be initialized in an order in strict accordance with that of
 their definitions _in a given translation unit_.  No guarantee is
 made for initializations across translation units.  However, GNU
 C++ allows users to control the order of initialization of objects
 defined at namespace scope with the init_priority attribute by
 specifying a relative PRIORITY, a constant integral expression
 currently bounded between 101 and 65535 inclusive.  Lower numbers
 indicate a higher priority.

 In the following example, `A' would normally be created before
 `B', but the `init_priority' attribute has reversed that order:

      Some_Class  A  __attribute__ ((init_priority (2000)));
      Some_Class  B  __attribute__ ((init_priority (543)));

 Note that the particular values of PRIORITY do not matter; only
 their relative ordering.

يمكنك القيام بأي مما يلي وفقًا للظروف.

  1. استخدم نمط المراقب كما اقترحه gurin.في الأساس يقوم المشرف بإبلاغ وحدات التحكم بأنها ستنخفض ...
  2. اجعل المشرف "يمتلك" وحدات التحكم ويكون مسؤولاً عن تدميرها عند تعطلها.
  3. احتفظ بوحدات التحكم في Shared_pointers حتى يتمكن من ينزل أخيرًا من إحداث الدمار الحقيقي.
  4. قم بإدارة كل من (المؤشرات الذكية لوحدات التحكم ) والمشرف على المكدس مما سيسمح لك بتحديد ترتيب التدمير
  5. آحرون ...

هل يمكن أن ننظر في استخدام عدد من وحدات تحكم مسجلة باسم الحارس للحذف الفعلي.

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

وكما ذكر هذا هو استخدام واحدة من نمط مراقب.

class Supervisor {
public:
    Supervisor() : inDeleteMode_(false) {}

    void deleteWhenDone() {
        inDeleteMode_ = true;
        if( controllers_.empty()){
            delete this;
        }
    }

    void deregister(Controller* controller) {
        controllers_.erase(
            remove(controllers_.begin(), 
                        controllers_.end(), 
                        controller));
        if( inDeleteMode_ && controllers_.empty()){
            delete this;
        }
    }


private:

    ~Supervisor() {}
    bool inDeleteMode_;
    vector<Controllers*> controllers_;
};

Supervisor* supervisor = Supervisor();
...
supervisor->deleteWhenDone();

وانها ليست أنيقة بالضبط، ولكن هل يمكن أن تفعل شيئا من هذا القبيل:

struct ControllerCoordinator {
    Supervisor supervisor;
    set<Controller *> controllers;

    ~ControllerDeallocator() {
        set<Controller *>::iterator i;
        for (i = controllers.begin(); i != controllers.end(); ++i) {
            delete *i;
        }
    }
}

وعالمي جديد:

ControllerCoordinator control;

وأينما بناء وحدة تحكم، إضافة control.supervisor.insert(controller). في كل مكان كنت تدمير واحد، إضافة control.erase(controller). كنت قد تكون قادرة على تجنب البادئة control. بإضافة مرجعية عالمية لcontrol.supervisor.

ولن تدمر عضو المشرف المنسق حتى بعد تشغيل المدمر، لذلك كنت يضمن لك أن مشرف سوف تعمر وحدات التحكم.

وماذا عن وجود المشرف رعاية التدمير وحدات تحكم؟

وطيب، كما اقترحت في مكان آخر جعل المشرف عن المفردة (أو كائن تسيطر similarily، أي راقب إلى جلسة).

استخدم الحراس المناسب (theading، الخ) حول المفرد إذا لزم الأمر.

// -- client code --
class ControllerClient {
public:
    ControllerClient() : 
        controller_(NULL)
        {
            controller_ = Controller::create();
        }

    ~ControllerClient() {
        delete controller_;
    }
    Controller* controller_;
};

// -- library code --
class Supervisor {
public: 
    static Supervisor& getIt() {        
        if (!theSupervisor ) {
            theSupervisor = Supervisor();
        }
        return *theSupervisor;
    } 

    void deregister(Controller& controller) {
        remove( controller );
        if( controllers_.empty() ) {
            theSupervisor = NULL;
            delete this;
        }       
    }

private:    
    Supervisor() {} 

    vector<Controller*> controllers_;

    static Supervisor* theSupervisor;
};

class Controller {
public: 
    static Controller* create() {
        return new Controller(Supervisor::getIt()); 
    } 

    ~Controller() {
        supervisor_->deregister(*this);
        supervisor_ = NULL;
    }
private:    
    Controller(Supervisor& supervisor) : 
        supervisor_(&supervisor)
        {}
}

وبينما القبيح هذا قد يكون أبسط طريقة:

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

وأشار

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

وأنا أفضل نمط مراقب وكذلك في هذه الحالة.

يمكنك استخدام الأحداث للإشارة إلى تدمير تحكم

إضافة WaitForMultipleObjects في المدمر المشرف الذي سوف ننتظر حتى يتم تدمير جميع وحدات التحكم.

في المدمر وحدات تحكم يمكنك رفع الحدث تحكم الخروج.

وتحتاج إلى الحفاظ على مجموعة عالمية من الحدث خروج مقابض لكل وحدة تحكم.

تأكد المشرف أتحكم في singelton. تأكد من أن منشئ السيطرة يحصل المشرف أثناء البناء (لا afterwords). هذا يضمن أن المشرف السيطرة هي التي شيدت بشكل كامل قبل عنصر التحكم. Thuse سوف يطلق المدمر قبل المدمر تحكم المشرف.

class CS
{
    public:
        static CS& getInstance()
        {
            static CS  instance;
            return instance;
        }
        void doregister(C const&);
        void unregister(C const&);
    private:
        CS()
        {  // initialised
        }
        CS(CS const&);              // DO NOT IMPLEMENT
        void operator=(CS const&);  // DO NOT IMPLEMENT
 };

 class C
 {
      public:
          C()
          {
              CS::getInstance().doregister(*this);
          }
          ~C()
          {
              CS::getInstance().unregister(*this);
          }
 };

ويحدد المعيار C ++ ترتيب التهيئة / تدمير عندما المتغيرات في السؤال عن تناسب في ملف واحد ( "وحدة الترجمة"). أي شيء التي تمتد أكثر من ملف واحد يصبح غير المحمولة.

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


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

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

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

وعندما قرأت العنوان على هذا السؤال سألت نفسي فورا "إذا كانت هناك طريقة أن أي كائن يمكن ضمان تدميره (دمر؟) الماضي، ثم ماذا سيحدث لو اعتمدت كائنين هذه الطريقة؟"

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