سؤال

أود أن تعريف ثابت شار* في رأس الملف لي .الملف cpp للاستخدام.حتى لقد حاول هذا:

private:
    static const char *SOMETHING = "sommething";

الذي يجلب لي خطأ برنامج التحويل البرمجي التالية:

خطأ C2864:'SomeClass::شيء' :فقط ثابت const جزءا لا يتجزأ من البيانات أعضاء يمكن تهيئة داخل الدرجة

أنا جديد في C++.ما الذي يجري هنا ؟ لماذا هذا غير قانوني ؟ وكيف يمكنك أن تفعل ذلك بدلا من ذلك?

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

المحلول

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

في رأس الخاص بك:

private:
    static const char *SOMETHING;
    static const int MyInt = 8; // would be ok

في الملف .cpp:

const char *YourClass::SOMETHING = "something";

وC ++ القياسية، 9.4.2 / 4:

<اقتباس فقرة>   

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

نصائح أخرى

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

عندما كائن يستخدم كمادة lvalue (أيكما أن شيئا عنوان في التخزين) ، فإنه يجب أن تستوفي "تعريف واحد القاعدة" (ODR)،.e يجب أن تكون محددة في واحد فقط وحدة الترجمة.المترجم لا تقرر أي وحدة الترجمة إلى تعريف هذا الكائن في.هذه هي مسؤوليتكم.قبل تعريف هذا الكائن في مكان ما كنت لا مجرد تحديد ذلك ، هي في الواقع يقول المترجم الذي ترغب في تحديده هنا, في هذا ترجمة خاصة وحدة.

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

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

والخطأ هو أنه لا يمكن تهيئة static const char* داخل الطبقة. يمكنك تهيئة فقط المتغيرات عدد صحيح هناك.

وتحتاج إلى تعريف متغير عضو في فئة، ومن ثم تهيئة ذلك خارج الصف:

و// ملف الرأس

class Foo {
    static const char *SOMETHING;
    // rest of class
};

و// ملف حزب الشعب الكمبودي

const char *Foo::SOMETHING = "sommething";

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

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

مع C++11 يمكنك استخدام constexpr الكلمة والكتابة في رأس الخاص بك:

private:
    static constexpr const char* SOMETHING = "something";


ملاحظات:

  • constexpr يجعل SOMETHING مؤشر ثابت بحيث لا يمكن الكتابة

    SOMETHING = "something different";
    

    في وقت لاحق.

  • اعتمادا على الخاص بك مترجم, قد تحتاج أيضا إلى كتابة تعريف صريح في .الملف cpp:

    constexpr const char* MyClass::SOMETHING;
    
class A{
public:
   static const char* SOMETHING() { return "something"; }
};

وأفعل ذلك في كل وقت - وخاصة لمعلمات CONST الافتراضي مكلفة

.
class A{
   static
   const expensive_to_construct&
   default_expensive_to_construct(){
      static const expensive_to_construct xp2c(whatever is needed);
      return xp2c;
   }
};

إذا كنت تستخدم Visual C ++، يمكنك غير تنقليا القيام بذلك باستخدام تلميحات إلى رابط ...

// In foo.h...

class Foo
{
public:
   static const char *Bar;
};

// Still in foo.h; doesn't need to be in a .cpp file...

__declspec(selectany)
const char *Foo::Bar = "Blah";

و__declspec(selectany) يعني أنه على الرغم من Foo::Bar سوف تحصل أعلن في ملفات الكائن متعددة، رابط سيختار فقط واحد.

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

وهناك خدعة يمكنك استخدامها مع قوالب لتقديم ملف H الثوابت فقط.

و(لاحظ أن هذا هو مثال القبيح، ولكن يعمل حرفيا في على الأقل في غرام ++ 4.6.1).

و(ملف values.hpp)

#include <string>

template<int dummy>
class tValues
{
public:
   static const char* myValue;
};

template <int dummy> const char* tValues<dummy>::myValue = "This is a value";

typedef tValues<0> Values;

std::string otherCompUnit(); // test from other compilation unit

و(MAIN.CPP)

#include <iostream>
#include "values.hpp"

int main()
{
   std::cout << "from main: " << Values::myValue << std::endl;
   std::cout << "from other: " << otherCompUnit() << std::endl;
}

و(other.cpp)

#include "values.hpp"

std::string otherCompUnit () {
   return std::string(Values::myValue);
}

وترجمة (مثل ز ++ -o MAIN.CPP الرئيسي other.cpp && ./main) ونرى اثنين من وحدات تجميع الرجوع إلى نفس ثابت أعلن في الرأس:

from main: This is a value
from other: This is a value

في MSVC، يمكنك بدلا من ذلك قد تكون قادرا على استخدام __declspec (selectany)

وعلى سبيل المثال:

__declspec(selectany) const char* data = "My data";

ومهيئ المستمر المسموح به من قبل C ++ القياسية فقط لأنواع لا يتجزأ أو التعداد. انظر 9.4.2 / 4 لمزيد من التفاصيل:

<اقتباس فقرة>   

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

و9.4.2 / 7:

<اقتباس فقرة>   

ويتم تهيئة ودمرت تماما مثل الكائنات غير المحلية (3.6.2، 3.6.3).

وأعضاء البيانات ثابت

وهكذا يجب أن تكتب في مكان ما في ملف حزب الشعب الكمبودي:

const char* SomeClass::SOMETHING = "sommething";

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

وهذا لا يفسر تماما لماذا يسمح لهم كما initializors في نوع، ولكن أعتقد أنها في الأساس #define وبعد ذلك لن يكون له معنى كجزء من نوع وليس جزءا من الكائن.

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