programming - c++ شرح




لماذا لا يمكنني الحصول على عضو ثابت ثابت غير متكامل في الفصل؟ (4)

لاحظت C ++ لن تجميع ما يلي:

class No_Good {
  static double const d = 1.0;
};

ومع ذلك ، فإنه يسمح لحسن الحظ بالتغيير حيث يتم تغيير المضاعفة إلى int أو غير موقعة أو أي نوع متكامل:

class Happy_Times {
  static unsigned const u = 1;
};

كان الحل هو تغييره ليصبح كما يلي:

class Now_Good {
  static double d() { return 1.0; }
};

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

لماذا يسمح لي مصمم (مصمم) C ++ الثابتة const أو int غير الموقعة ، ولكن ليس مزدوج؟

تحرير: أنا أستخدم visual studio 7.1 (.net 2003) على نظام التشغيل Windows XP.

Edit2:

تم الإجابة على السؤال ، ولكن لإكماله ، الخطأ الذي رأيته:

error C2864: 'd' : only const static integral data members can be initialized inside a class or struct

أنا لا أعرف لماذا تعامل مضاعفة مختلفة من كثافة العمليات. ظننت أنني استخدمت هذا الشكل من قبل. فيما يلي حل بديل:

class Now_Better
{
    static double const d;
};

وفي ملف .cpp الخاص بك:

double const Now_Better::d = 1.0;

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

باستخدام g + بدون تحسين ( -O0 ) ، فإنه يحد تلقائياً من المتغيرات الصحيحة الثابتة ولكن ليس قيم مزدوجة ثابتة. في مستويات تحسين أعلى (على سبيل المثال -O1 ) ، فإنه يؤكد الثنائيات المستمرة. وبالتالي ، يتم تجميع التعليمة البرمجية التالية في -O1 ولكن ليس في -O0 :

// File a.h
class X
{
 public:
  static const double d = 1.0;
};

void foo(void);

// File a.cc
#include <stdio.h>

#include "a.h"

int main(void)
{
  foo();
  printf("%g\n", X::d);

  return 0;
}

// File b.cc
#include <stdio.h>

#include "a.h"

void foo(void)
{
  printf("foo: %g\n", X::d);
}

سطر الأوامر:

g++ a.cc b.cc -O0 -o a   # Linker error: ld: undefined symbols: X::d
g++ a.cc b.cc -O1 -o a   # Succeeds

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


لا يعطي حقاً مبررًا ، ولكن إليك ما يقوله Stroustrup عن هذا في "C ++ Programming Language Third Edition":

10.4.6.2 ثوابت الأعضاء

من الممكن أيضاً تهيئة عضو ثابت ثابت ثابتة بإضافة مُهيئ تعبير ثابت إلى إعلان العضو الخاص به. فمثلا:

class Curious {
    static const int c1 = 7;        // ok, but remember definition
    static int c2 = 11;             // error: not const
    const int c3 = 13;              // error: not static
    static const int c4 = f(17);    // error: in-class initializer not constant
    static const float c5 = 7.0;    // error: in-class not integral
    // ...
};

ومع ذلك ، يجب أن يتم تعريف العضو (الذي تم إنشاؤه بشكل فريد) في مكان ما ، وقد لا يتكرر المُبدِّل:

const int Curious::c1;  // necessary, but don't repeat initializer here

أنا أعتبر هذا غير لائق. عندما تحتاج إلى ثابت رمزي داخل تعريف الفصل ، استخدم العداد (4.8 ، 14.4.6 ، 15.3). فمثلا:

class X {
    enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 };
    // ...
};

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

وفي التذييل C (Technicalities) في القسم C.5 (تعبيرات متواصلة) ، Stroustrup لديه هذا ليقول عن "التعبيرات الثابتة":

في أماكن مثل حدود الصفيف (5.2) ، تسميات الحالة (6.3.2) ، والمبدئيات للعدادات (4.8) ، يتطلب C ++ تعبير ثابت . يقيّم التعبير الثابت ثابت ثابت أو تعداد. يتألف هذا التعبير من حرفيّات (4.3.1 ، 4.4.1 ، 4.5.1) ، معّدون (4.8) ، والثوابت تبدّلها التعبيرات الثابتة. في قالب ، يمكن أيضًا استخدام معلمة قالب صحيح (C.13.3). لا يمكن استخدام القيم الحرفية العائمة (4.5.1) إلا إذا تم تحويلها صراحةً إلى نوع متكامل. يمكن استخدام الدالات ، كائنات الفئة ، المؤشرات ، والمراجع كمعاملات إلى مشغل sizeof (6.2) فقط.

حدسي ، التعبيرات الثابتة هي تعبيرات بسيطة يمكن تقييمها بواسطة المترجم قبل ربط البرنامج (9.1) ويبدأ في التشغيل.

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


هنا هو فهمي على أساس بيان Stroustrup حول تعريف في الصنف

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

http://www.stroustrup.com/bs_faq2.html#in-class

بشكل أساسي ، هذا غير مسموح لأن C ++ لا تسمح بذلك. من أجل جعل قواعد رابط أكثر بساطة ، يتطلب C ++ أن كل كائن له تعريف فريد.

العضو الثابت له مثيل واحد فقط في نطاق الفئة ، وليس مثل المتغيرات الثابتة العادية المستخدمة بكثافة في C ، والتي تحتوي على instatnce واحد فقط داخل وحدة ترجمة واحدة.

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

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

من أجل تقليل المضاعفات وإعطاء الوظيفة الأساسية ، يوفر C ++ التعريف الوحيد في الفئة لثابت ثابت من نوع التعداد أو التعداد.







c++