سؤال

في لغة C++ هل يوجد فرق بين:

struct Foo { ... };

و

typedef struct { ... } Foo;
هل كانت مفيدة؟

المحلول

في C ++، ليس هناك سوى فرق دقيق. انها المستمر من C، الذي يحدث فرقا.

والمعيار لغة C ( C89 §3.1.2.3 ، C99 §6.2.3 ، و <لأ href = " http://port70.net/~nsz/c/c11/n1570.html#6.2.3 "يختلط =" noreferrer "> C11 §6.2.3 ) ولايات بمساحات منفصلة لمختلف فئات معرفات، بما في ذلك < م> العلامة معرفات struct / union / enum) و <م> معرفات العادية typedef ومعرفات أخرى).

إذا قلته للتو:

struct Foo { ... };
Foo x;

وستحصل خطأ المترجم، لأنه يتم تعريف Foo فقط في علامة مساحة الاسم.

وأنت تريد أن نعلن ذلك على النحو التالي:

struct Foo x;

وأي وقت تريد للإشارة إلى Foo، وكنت دائما أن نسميها struct Foo. هذا يحصل مزعج سريع، بحيث يمكنك إضافة typedef:

struct Foo { ... };
typedef struct Foo Foo;

والآن struct Foo (في العلامة مساحة الاسم) وعادل Foo عادي (في مساحة الاسم المعرف العاديين) على حد سواء تشير إلى نفس الشيء، ويمكنك يعلن بحرية كائنات من نوع Foo دون الكلمة struct.


ووبناء:

typedef struct Foo { ... } Foo;

وهو مجرد اختصار لإعلان وtypedef.


وأخيرا،

typedef struct { ... } Foo;

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


في C ++، جميع الإعلانات struct / union / enum / class تعمل كما هي typedef'ed ضمنا، طالما ليست خفية الاسم بإعلان آخر يحمل نفس الاسم. انظر مايكل بير في الإجابة للحصول على التفاصيل الكاملة .

نصائح أخرى

في هذه المادة DDJ, يشرح دان ساكس منطقة صغيرة يمكن أن تتسلل فيها الأخطاء إذا لم تقم بكتابة بنياتك (والفئات!):

إذا كنت تريد ، يمكنك أن تتخيل أن C ++ ينشئ typedef لكل اسم علامة ، مثل

typedef class string string;

لسوء الحظ ، هذا ليس دقيقًا تمامًا.أتمنى لو كان الأمر بهذه البساطة ، لكنه ليس كذلك.لا يمكن لـ C ++ إنشاء مثل هذه الطباعات للهياكل أو النقابات أو التعدادات دون إدخال عدم توافق مع C.

على سبيل المثال ، لنفترض أن برنامج C يعلن كلاً من الوظيفة وحالة الهيكل المسمى:

int status(); struct status;

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

إذا قام C ++ تلقائيًا بإنشاء typedefs للعلامات ، فعندما تقوم بتجميع هذا البرنامج كـ C ++ ، فإن برنامج التحويل البرمجي سيولد:

typedef struct status status;

لسوء الحظ ، سيتعارض اسم هذا النوع مع اسم الوظيفة ، ولن يتم تجميع البرنامج.لهذا السبب لا يمكن لـ C ++ ببساطة إنشاء typedef لكل علامة.

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

وبالتالي فإن برنامج C يحتوي على كل من:

int status(); struct status;

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

فكيف يسمح هذا لبق أن يتسلل إلى البرامج؟النظر في البرنامج في القائمة 1.يحدد هذا البرنامج فئة FOO مع مُنشئ افتراضي ، ومشغل تحويل يحول كائن FOO إلى CONST *.التعبير

p = foo();

في MAIN يجب بناء كائن FOO وتطبيق مشغل التحويل.بيان الإخراج اللاحق

cout << p << '\n';

يجب عرض فئة FOO ، لكنه لا.يعرض وظيفة foo.

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

p = class foo();

تتمثل طريقة تجنب هذا الالتباس في جميع أنحاء البرنامج في إضافة typedef التالي لاسم الفصل FOO:

typedef class foo foo;

مباشرة قبل أو بعد تعريف الفصل.يتسبب Typedef هذا في تعارض بين اسم النوع FOO واسم الوظيفة FOO (من المكتبة) الذي سيؤدي إلى خطأ في وقت الترجمة.

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

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

وأكثر واحد فارق مهم: typedefs لا يمكن الإعلان إلى الأمام. حتى لخيار typedef يجب #include الملف الذي يحتوي على typedef، وهذا يعني أن كل شيء #includes .h الخاص أيضا يتضمن هذا الملف ما إذا كان يحتاج مباشرة أو لا تصدق، وهلم جرا. ويمكن أن يؤثر بالتأكيد مرة الإنشاء الخاصة بك على مشاريع أكبر.

وبدون typedef، في بعض الحالات يمكنك فقط إضافة إعلان إلى الأمام من struct Foo; في الجزء العلوي من ملف .h الخاص بك، و#include فقط تعريف البنية في ملف .cpp الخاص بك.

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

7.1.3 محدد typedef

1 [...]

الاسم المعلن باستخدام محدد typedef يصبح اسم typedef.ضمن نطاق إعلانها ، يكون اسم typedef مكافئًا بشكل نحلي للكلمة الرئيسية ويسمي النوع المرتبط بالمعرف بالطريقة الموضحة في الفقرة 8.وبالتالي فإن اسم typedef هو مرادف لنوع آخر.اسم typedef لا يقدم نوعا جديدا بالطريقة التي يعمل بها إعلان الفئة (9.1) أو إعلان التعداد.

8 إذا عرف إعلان Typedef فئة غير اسم (أو تعداد) ، فسيتم استخدام أول اسم Typedef-name من خلال الإعلان أنه نوع الفئة (أو نوع التعداد) للدلالة على نوع الفئة (أو نوع التعداد) لأغراض الربط فقط ( 3.5).[ مثال:

typedef struct { } *ps, S; // S is the class name for linkage purposes

لذلك، محرف دائماً يُستخدم كعنصر نائب/مرادف لنوع آخر.

ولا يمكنك استخدام الأمام إعلان مع البنية typedef و.

ووالبنية نفسها هي نوع مجهول، لذلك لم يكن لديك الاسم الفعلي إلى الأمام تعلن.

typedef struct{
    int one;
    int two;
}myStruct;

وإعلان الأمام مثل هذا العمل متعود:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

والبنية هو خلق نوع البيانات. وtypedef وهو وضع اسم مستعار لنوع البيانات.

وهناك فرق هام بين 'البنية typedef و' و 'البنية' في C ++ هو أن initialisation عضوا مضمنة في "البنيات typedef و" لن ينجح.

// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;

// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };

وليس هناك فرق في C ++، ولكن أعتقد في C انها ستسمح لك ليعلن مثيلات فو البنية دون أن تفعل صراحة:

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