[c++] إعادة توجيه التعداد في c ++



Answers

من الممكن أيضا تعريف التعداد للأمام في C ++ 0x. في السابق ، كان السبب في عدم تحديد أنواع التعداد هو أن حجم التعداد يعتمد على محتوياته. طالما تم تحديد حجم التعداد بواسطة التطبيق ، يمكن أن يتم الإعلان عنه:

enum Enum1;                   //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int;    //Legal in C++0x.
enum class Enum3;             //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short;  //Illegal in C++0x, because Enum2 was previously declared with a different type.
Question

أحاول القيام بشيء مثل ما يلي:

enum E;

void Foo(E e);

enum E {A, B, C};

الذي يرفض المترجم. لقد تلقيت نظرة سريعة على Google ويبدو أن الإجماع "لا يمكنك فعل ذلك" ، ولكن لا يمكنني فهم السبب. يمكن لأي شخص أن يفسر؟ تشكرات.

توضيح 2: أنا أفعل ذلك لأن لدي طرق خاصة في فصل دراسي يأخذ التعداد المذكور ، ولا أريد أن تتعرض قيم التعداد - لذلك ، على سبيل المثال ، لا أريد أن يعرف أي شخص أن E يتم تعريفه على أنه

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

لأن المشروع X ليس شيئًا أريد أن يعرفه المستخدمون.

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

أما بالنسبة للالمجمع - انها دول مجلس التعاون الخليجي.




سيكون الحل لمشكلتك إما:

1 - استخدم int بدلاً من enums: قم بتعريف ints الخاص بك في مساحة اسم مجهول في ملف CPP الخاص بك (ليس في رأس الصفحة):

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

نظرًا لأن أساليبك خاصة ، فلن يعبث أي شخص بالبيانات. يمكنك أيضًا الذهاب إلى أبعد من ذلك لاختبار ما إذا أرسل إليك شخص ما بيانات غير صالحة:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2: إنشاء فئة كاملة مع عمليات بحث Const محدودة ، كما هو الحال في Java. قم بالإعلان عن الفئة ، ثم قم بتعريفها في ملف CPP ، وقم بتثبيتها فقط على قيم مثل التعداد. فعلت شيئا من هذا القبيل في C ++ ، وكانت النتيجة غير مرضية كما هو مطلوب ، لأنها تحتاج إلى بعض التعليمات البرمجية لمحاكاة التعداد (بناء نسخ ، عامل = ، الخ).

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

تخميني سيكون إما الحل 3 أو 1.




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

لذا لا يستطيع المترجم حتى أن يجعلك تعيد تعريف التعداد والمستخدم بمؤشر ، لأنه حتى هناك ، يحتاج إلى حجم التعداد.




تقوم بتعريف تعداد لتقييد القيم المحتملة لعناصر من النوع إلى مجموعة محدودة. يتم فرض هذا التقييد في وقت التحويل البرمجي.

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

على الرغم من أن المحول البرمجي قلقة حول حجم نوع تعداد ، يتم فقد الهدف من التعداد عند إعادة توجيهها.




إن الإعلان عن الأشياء في C ++ أمر مفيد للغاية لأنه يسرع بشكل كبير وقت التجميع . يمكنك إعادة الإعلان عن العديد من الأشياء في C ++ بما في ذلك: struct ، struct ، function ، إلخ ...

ولكن هل يمكنك إعادة الإعلان عن enum في لغة C ++؟

لا لا يمكنك ذلك.

لكن لماذا لا نسمح بذلك؟ إذا تم السماح لك بتحديد نوع enum في ملف الرأس الخاص بك ، وقيم enum في الملف المصدر. يبدو أنه يجب أن يسمح الصحيح؟

خطأ.

في C ++ لا يوجد نوع افتراضي enum كما هو الحال في C # (int). في C ++ سيتم تحديد نوع enum الخاص بك بواسطة المحول البرمجي ليكون أي نوع يناسب مجموعة القيم التي لديك enum الخاص بك.

ماذا يعني ذالك؟

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

معيار ISO C ++ S7.2.5:

النوع الأساسي من التعداد هو نوع متكامل يمكن تمثيل كافة قيم العداد المعرّفة في التعداد. يتم تعريف التنفيذ أي نوع متكامل يتم استخدامه كنوع أساسي من أجل التعداد باستثناء أن النوع الأساسي يجب ألا يكون أكبر من int إلا إذا كانت قيمة العداد لا يمكن احتواؤها في int أو unsigned int . إذا كانت قائمة العداد فارغة ، فإن النوع الأساسي هو كما لو أن التعداد كان له عداد واحد ذا قيمة 0. قيمة sizeof() المطبقة على نوع التعداد ، أو نوع من أنواع العد ، أو العداد ، هي قيمة sizeof() المطبقة على النوع الأساسي.

يمكنك تحديد حجم نوع تعداد في C ++ باستخدام مشغل sizeof . حجم نوع تعداد هو حجم نوع الأساسي الخاص به. وبهذه الطريقة يمكنك تخمين ما هو نوع المترجم الذي تستخدمه enum الخاص بك.

ماذا لو حددت نوع enum الخاص بك بوضوح مثل هذا:

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

يمكنك بعد ذلك إلى الأمام تعلن enum الخاص بك؟

لا ، لكن لم لا؟

إن تحديد نوع enum ليس في الواقع جزءًا من معيار C ++ الحالي. هو تمديد VC ++. سيكون جزءًا من C ++ 0x بالرغم من ذلك.

Source




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

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

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




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

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

معيار C ++ الحالي لا يسمح صراحة بعمل شيء مثل

enum X;

(في 7.1.5.3/1 ). لكن معيار C ++ القادم بسبب العام القادم يسمح بما يلي ، مما أقنعني بأن المشكلة تتعلق في الواقع بالنوع الأساسي:

enum X : int;

يُعرف باسم إعلان التعداد "غير الشفاف". يمكنك حتى استخدام X بالقيمة في التعليمة البرمجية التالية. ويمكن لاحقاً تعريف حامليها في إعادة توزيع لاحقة للتعداد. انظر 7.2 في مسودة العمل الحالية.




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

إنها تقنية تضمن إخفاء الطبقة الداخلية في العناوين عن طريق التصريح:

class A 
{
public:
    ...
private:
    void* pImpl;
};

ثم في ملف التنفيذ الخاص بك (cpp) ، تقوم بتعريف فئة سيكون تمثيل internals.

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

يجب إنشاء التنفيذ بشكل ديناميكي في مُنشئ الفئة وحذفه في destructor وعند تنفيذ الطريقة العامة ، يجب عليك استخدام:

((AImpl*)pImpl)->PrivateMethod();

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

ولكن من المثير للاستخدام ، لذلك ينبغي عليك أن تسأل نفسك إذا ما كان مجرد إعلان التعداد الخاص بك في رأس الصفحة هو مشكلة كبيرة.




بالنسبة إلى VC ، إليك الاختبار المتعلق بالإعلان المتقدم وتحديد النوع الأساسي:

  1. يتم ترجمة التعليمات البرمجية التالية موافق.
    typedef int myint;
    enum T ;
    void foo(T * tp )
    {
        * tp = (T)0x12345678;
    }
    enum T : char
    {
        A
    };

ولكن حصلت على تحذير ل / W4 (/ W3 لا تكبد هذا التحذير)

تحذير C4480: ملحق غير قياسي مستخدم: تحديد النوع الأساسي للتعداد 'T'

  1. VC (Microsoft (R) 32-bit C / C ++ Optimizing Compiler Version 15.00.30729.01 for 80x86) يبدو buggy في الحالة أعلاه:

    • عند رؤية التعداد T ؛ يفترض VC نوع التعداد يستخدم T بايت 4 بايت الافتراضي كـ النوع الأساسي ، بحيث يكون رمز التجميع الذي تم إنشاؤه:
    ?foo@@YAXPAW4T@@@Z PROC                 ; foo
    ; File e:\work\c_cpp\cpp_snippet.cpp
    ; Line 13
        push    ebp
        mov ebp, esp
    ; Line 14
        mov eax, DWORD PTR _tp$[ebp]
        mov DWORD PTR [eax], 305419896      ; 12345678H
    ; Line 15
        pop ebp
        ret 0
    ?foo@@YAXPAW4T@@@Z ENDP                 ; foo

يتم استخراج رمز التجميع المذكور أعلاه من /Fatest.asm مباشرة ، وليس تخميني الشخصي. هل ترى في mov DWORD PTR [eax] ، 305419896؛ خط 12345678H؟

يوضح مقتطف الشفرة التالي:

    int main(int argc, char *argv)
    {
        union {
            char ca[4];
            T t;
        }a;
        a.ca[0] = a.ca[1] = a.[ca[2] = a.ca[3] = 1;
        foo( &a.t) ;
        printf("%#x, %#x, %#x, %#x\n",  a.ca[0], a.ca[1], a.ca[2], a.ca[3] );
        return 0;
    }

والنتيجة هي: 0x78 ، 0x56 ، 0x34 ، 0x12

  • بعد إزالة الإعلان الأمامي للتعداد T ونقل تحريك الدالة foo بعد تعريف التعداد T: النتيجة موافق:

يصبح التعليم الأساسي أعلاه:

mov BYTE PTR [eax]، 120؛ 00000078H

النتيجة النهائية هي: 0x78 ، 0x1 ، 0x1 ، 0x1

لاحظ أنه لا يتم الكتابة فوق القيمة

لذلك يعتبر استخدام التعليمة المستقبلية للتعداد في VC ضارًا.

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




Related



Tags

c++ c++   enums