هل هو موافق استخدام متغير ثابت تهيئة/تسجيل المتغيرات ؟

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

  •  22-09-2019
  •  | 
  •  

سؤال

اللغة:C++ الأدوات:Qt4

الأدوات أنا باستخدام لديه أسلوب ثابت يسمى int QEvent::registerEventType() للتسجيل بلدي أنواع الأحداث.عندما فرعية هذا QEvent لا تحتاج إلى توفير قاعدة الطبقة هذه القيمة. QEvent::QEvent(int type).

هل هو موافق استخدام متغير ثابت لهذه الكلمة قبل التطبيق يبدأ ؟ النظر في ما يلي:

//This is all in my .cpp file

static int myEventType;  //This will contain my registered type

/*If I create a static instance of this class the constructor 
  gets called before the main() function starts.
*/
class DoRegisterMyEventType {  
public:
  DoRegisterMyEventType() {
    myEventType = QEvent::registerEventType();
  }
};

static DoRegisterMyEventType doRegisterMyEventType;

//Here is the constructor for MyEvent class which inherits QEvent.
MyEvent::MyEvent()
  : QEvent(myEventType)
{
}

كيف "الشر" هو هذا ؟ أنا يمكن أن التفاف كل شيء في مساحة لمنع تلويث مساحة العالمي.

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

المحلول

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

static inline int GetMyEventType()
{
    static int sEventType = QEvent::registerEventType();
    return sEventType;
}

MyEvent::MyEvent()
  : QEvent(GetMyEventType())
{
}

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

إليك نسخة آمنة مؤشرات ترابط ، استنادًا إلى Boost :: Call_once:

#include "boost/thread/once.hpp"

static boost::once_flag sHaveRegistered = BOOST_ONCE_INIT; //This is initialized statically, effectively at compile time.    
static int sEventType = -1; //-1 is not a valid event

static void DoRegister()
{
    sEventType = QEvent::registerEventType();
}

static inline int GetMyEventType()
{
    boost::call_once(sHaveRegistered, &DoRegister);
    return sEventType;
}

نصائح أخرى

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

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

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

// the implementation file for main, could be named main.cpp

#include "whatever_declares_the_real_main.hpp"

#include "global_objects.inc"

int main(int argc, char* argv[]) try {
#include "main_init.inc"

  return the_real_main(argc, argv);

  // main.cpp has well-defined responsibility:
  // initialize global state before passing control to another function, and
  // handle return-code or exceptions

  // you can modify this, depending on your preference and desired API
  // for example:
  return the_real_main(std::vector<std::string>(argv+1, argv+argc));
  return the_real_main(parse_args(argv+1, argv+argc));
  // just make sure to keep main.cpp's responsibility well-defined and
  // relatively simple
}
// example handling; depending on your specifics, you might do something
// different, or know how to provide more information:
catch (std::exception& e) {
  std::cerr << "abnormal termination: " << e.what() << '\n';
  return 1;
}
catch (...) {
  std::cerr << "abnormal termination.\n";
  return 1;
}

هذه .inc الملفات لا رؤوس ولا تنفيذ الملفات.بالضبط امتداد الملف لا يهم طالما أنك لا تستخدم شيئا مما يشيع استخدام رؤوس أو تنفيذ ملفات مثل .ساعة, في السماء،.hpp،.cc،.cpp, وهكذا دواليك.يمكنك توليد global_objects.inc و main_init.inc القائم قبالة تسمية ملف الاتفاقيات باستخدام تشمل الحراس بحيث تبعيات قد تكون شملت (تماما كما تشمل حراس العمل على رؤوس).

على سبيل المثال, كل من هذه الملفات تتوافق مع myevent.hpp و سيتم وضعها إلى جانب الرأس:

// file "myevent.global_inc"
#ifndef INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868
#define INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868

#include <QEvent.hpp> // or whatever headers you need

#include "myevent.hpp" // declares the variable defined just below
// (remember you use 'extern' to declare objects without defining them)

int your_namespace::myEventType = QEvent::registerEventType();

#endif

// file "myevent.main_inc"
#ifndef INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81
#define INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81

// nothing needed in this case, from what you've shown so far

// this is where you place expressions that would otherwise require a dummy
// global variable to make sure they are executed, but this also allows use
// of temporary variables while includes handle dependency order:
#include "something_else.main_inc" // fake example dependency, which must
{                                  // be executed first
  int temp;
  some_func(&temp);
  other_func(temp); // not easy to transform this into a global's init
  // expression, yet defining it this way is natural, because it's exactly
  // how you would do it inside a function
}

#endif

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

أستخدم نمط "كائن السجل الثابت" قليلاً ، ولكن يجب أن تكون على دراية بمشكلة كبيرة واحدة - يجب أن تتأكد . نظرًا لأن C ++ لا يضمن ترتيب البناء الثابت بين وحدات الترجمة ، فقد يكون هذا مشكلة. أحد الحلول هو استخدام ما يسمى Meyer Singleton:

class Registry {
  public:
    static Registry & Instance() {
        static Registry r;
        return r;
    }

    ... 

 private:
    Registry() {    
      ...
    }
};

نظرًا لأن جميع الإشارات إلى السجل يجب أن تمر من خلال طريقة المثيل () ، فأنت مضمون أمر البناء المطلوب.

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