سؤال

لدي فئة قالب محددة في ملف رأس مثل هذا.لقد قمت هنا بتعريف متغير ثابت أيضًا:

#ifndef TEST1_H_
#define TEST1_H_

void f1();

static int count;

template <class T>
class MyClass
{
public:

    void f()
    {
        ++count;
    }


};

#endif

وقمت بتعريف الدالة main() في ملف cpp مختلف مثل هذا:

int main(int argc, char* argv[])
{
    MyClass<int> a;
    a.f();
    f1();

    cout<<"Main:" << count << "\n";

    return 0;
}

لقد قمت بتنفيذ الدالة f1() في ملف cpp مختلف مثل هذا:

void f1()
{
    MyClass<int> a;
    a.f();

    cout<<"F1: " <<count <<"\n";
}

عندما قمت بتجميع هذا باستخدام VC6، حصلت على الإخراج كـ "F1:0 Main:2".كيف يكون هذا ممكنا؟أيضًا، بشكل عام، كيف يجب أن أتعامل إذا كنت أرغب في استخدام المتغيرات الثابتة مع القوالب؟

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

المحلول

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

أعتقد أن ما تريده حقًا هنا هو متغير عضو القالب الثابت المرتبط بكل منها مثال من فئة القالب.انها تبدو مثل هذا:

template <class T>
class MyClass
{
    // static member declaration
    static int count;
    ...
};

// static member definition
template<class T> int MyClass<T>::count = 0;

سيؤدي هذا إلى الحصول على حساب لكل إنشاء مثيل للقالب الخاص بك.وهذا هو، سيكون لديك العد ل MyClass<int>, MyClass<foo>, MyClass<bar>, ، إلخ. f1() سيبدو الآن مثل هذا:

void f1() {
    MyClass<int> a;
    a.f();

    cout<<"F1: " << MyClass<int>::count <<"\n";
}

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

ومع ذلك، ربما لا تريد متغيرًا عامًا مباشرةً لأنك تخاطر باستخدامه قبل تهيئته.يمكنك التغلب على ذلك عن طريق إنشاء طريقة ثابتة عامة تُرجع مرجعًا لعددك:

int& my_count() {
    static int count = 0;
    return count;
}

ثم الوصول إليه من داخل صفك مثل هذا:

void f() {
    ++my_count();
}

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

نصائح أخرى

وسوف وضع إعلان ثابت في ملف رأس تسبب كل ملف .cpp للحصول على نسختها الخاصة من المتغير. حتى البيانين cout تطبع المتغيرات المختلفة.

هل كنت تتوقع "F1: 1 الرئيسية: (1)"؟ لك مثيل MyClass<int> في وحدتين الترجمة منفصلة (أي ملفين الكائن)، ورأى أن هناك رابط قالب مثيل مكررة، لذلك تجاهل مثيل التي كانت في ملف الكائن f1 ل.

هل يمر /OPT:ICF أو /OPT:REF إلى رابط VC6؟ التي قد تكون ذات صلة إزالة قالب مثيل مكررة (أم لا؛ تكرار التجسيدات قالب قد يكون حالة خاصة، بالمقارنة مع وظائف مكررة العادية). يبدو دول مجلس التعاون الخليجي للقيام شيء ما شابه ذلك على بعض المنابر.

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

وهناك حل آخر، يمكنك إنشاء فئة أصل مشترك، ووضع هذا المتغير الثابت في ذلك، ثم جعل الطبقة القالب ترث من القطاع الخاص، وهنا مثال:

class Parent
{
protected: 
    static long count;
};

long Parent::count = 0;

template<typename T>
class TemplateClass: private Parent
{
private: 
    int mKey;
public:
    TemplateClass():mKey(count++){}
    long getKey(){return mKey;}
}

int main()
{
    TemplateClass<int> obj1;
    TemplateClass<double> obj2;

    std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl;
    std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl;

    return 0;
}

والناتج سيكون:

Object 1 key is: 0 
Object 2 key is: 1

أعتقد أن هذا هو في الواقع سلوك غير محدد.

وفقًا لـ C++ 14 [basic.def.odr]/6:

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

  • يجب أن يتكون كل تعريف لـ D من نفس تسلسل الرموز المميزة؛و
  • في كل تعريف لـ D ، يجب أن تشير الأسماء المقابلة ، وفقًا لـ 3.4 ، إلى كيان محدد في تعريف D ، أو يشير إلى نفس الكيان ، بعد دقة الحمل الزائد (13.3) وبعد مطابقة تخصص القالب الجزئي (14.8 .3) ، باستثناء أن الاسم يمكن أن يشير إلى كائن const غير متطابق مع ارتباط داخلي أو معدوم إذا كان للكائن نفس النوع الحرفي في جميع تعريفات d ، ويتم تهيئة الكائن بتعبير ثابت (5.19) ، و الكائن ليس باستخدام ODR ، والكائن له نفس القيمة في جميع تعريفات D ؛[...]

المشكلة هي أنه في الأول .cpp الملف، الاسم count داخل f1 يشير إلى كائن مختلف عن الاسم count داخل f1 في الثانية .cpp الملف، مما ينتهك شرط أن تشير الأسماء المقابلة إلى نفس الكيان.

فهي كائنات مختلفة بسبب static المحدد الذي ينص على أن كل وحدة ترجمة تحصل على كائن خاص بها بهذا الاسم.

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