سؤال

أنا أحاول أن أفعل شيئا مثل التالية:

enum E;

void Foo(E e);

enum E {A, B, C};

التي مترجم ترفض.لقد كانت نظرة سريعة على جوجل و يبدو أن الآراء "لا يمكنك أن تفعل ذلك", ولكن لا أستطيع أن أفهم لماذا.يمكن لأي شخص أن أشرح ؟

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

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

كما project X ليست شيئا كنت تريد المستخدمين أن تعرف عن.

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

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

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

المحلول

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


من قسم 7.2.5 ISO C++ القياسية:

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

منذ المتصل إلى وظيفة يجب أن تعرف الأحجام من المعلمات بشكل صحيح إعداد مكدس الاستدعاءات عدد من التعدادات في التعداد القائمة يجب أن تكون معروفة من قبل وظيفة النموذج.

تحديث:في C++0X بناء الجملة من أجل foreward معلنا أنواع التعداد وقد اقترح ومقبولة.يمكنك أن ترى في الاقتراح http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf

نصائح أخرى

إلى الأمام إعلان enums من الممكن أيضا في 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.

انا إضافة ما يصل إلى تاريخ الإجابة هنا ، في ضوء التطورات الأخيرة.

يمكنك إعادة توجيه-تعلن التعداد في C++11, طالما أنك تعلن نوع التخزين في نفس الوقت.الجملة تبدو مثل هذا:

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

في الواقع ، إذا كانت وظيفة لا يشير إلى قيم التعداد ، لا تحتاج كاملة الإعلان على الإطلاق في تلك المرحلة.

هذا معتمد من قبل G++ 4.6 فصاعدا (-std=c++0x أو -std=c++11 في أحدث الإصدارات).Visual C++ 2013 يدعم هذا ؛ في الإصدارات السابقة كان لديه نوع من عدم الدعم القياسي الذي لم أعرف حتى الآن لقد وجدت بعض الاقتراحات البسيطة إلى الأمام الإعلان القانونية ، ولكن YMMV.

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

ولكن هل لك إلى الأمام تعلن enum في C++?

لا يمكنك.

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

من الخطأ.

في C++ هناك أي تقصير نوع enum وكأنه لا يوجد في C# (الباحث).في 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 على الرغم من.

المصدر

[جوابي خاطئ ، ولكن تركت ذلك هنا لأن تعليقات مفيدة].

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

في الممارسة, على الأقل في جميع المجمعين الشعبية مؤشرات enums هي متناسقة الحجم.إلى الأمام إعلان enums هو توفير تمديد اللغة من قبل Visual C++ ، على سبيل المثال.

هناك في الواقع أي شيء مثل قدما إعلان التعداد.كما التعداد تعريف لا تحتوي على أي رمز يمكن أن تعتمد على غيرها من التعليمات البرمجية باستخدام enum إنه عادة لا مشكلة في تعريف التعداد تماما عندما كنت أول من يعلن ذلك.

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

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

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

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

الحالية C++ القياسية صراحة لا يسمح تفعل شيئا مثل

enum X;

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

enum X : int;

كما هو معروف "مبهمة" enum الإعلان.يمكنك حتى استخدام X من حيث القيمة في البرمجية التالية.و التعداد يمكن أن تكون في وقت لاحق يحدد في وقت لاحق redeclaration من التعداد.انظر 7.2 في العمل الحالية مشروع.

كنت تفعل ذلك بهذه الطريقة:

[في العام رأس]

typedef unsigned long E;

void Foo(E e);

[في داخلي رأس]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

عن طريق إضافة FORCE_32BIT علينا أن نضمن أن للمحتوى الإلكتروني يجمع طويلة, حتى انها قابلة للتبديل مع E.

يبدو أنه لا يمكن أن يكون إلى الأمام-أعلن في دول مجلس التعاون الخليجي!

مناقشة مثيرة للاهتمام هنا

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

وهو أسلوب يضمن إخفاء الطبقة الداخلية في رؤوس فقط معلنا:

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

ثم في ملف التنفيذ (cpp) ، تعلن الدرجة التي سيتم تمثيل الداخلية.

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

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

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

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

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

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

يمكنك لف التعداد في البنية ، مضيفا في بعض المنشئات نوع التحويلات, و إلى الأمام تعلن البنية بدلا من ذلك.

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

يظهر هذا العمل:http://ideone.com/TYtP2

هناك بعض المعارضة منذ هذا صدم (نوعا ما), حتى هنا بعض ذات الصلة بت من المعيار.وتبين البحوث أن المعيار لا تحدد إلى الأمام إعلان ولا صراحة أنه enums يمكن أو لا يمكن أن يكون إلى الأمام المعلنة.

أولا من التنسيق والارتباط.التعداد القسم 7.2:

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

لذا الأساسية نوع من التعداد هو تنفيذ محددة ، مع قاصر واحد قيود.

القادمة ونحن نقف إلى قسم "غير مكتملة أنواع" (3.9), الذي هو أقرب نأتي إلى أي معيار الأمام الإعلانات:

الفئة التي أعلنت دون تعريف ، أو مجموعة غير معروفة الحجم أو من ناقصة نوع العنصر هو غير كامل-تحديد نوع الكائن.

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

لذلك هناك معيار تقريبا وضعت الأنواع التي يمكن أن تكون إلى الأمام المعلنة.التعداد لم يكن هناك, لذلك مترجم الكتاب عموما الصدد إلى الأمام معلنا كما ألغى المعيار تعزى لمتغير حجم الكامنة نوع.

فمن المنطقي أيضا.Enums عادة المشار إليها في قيمة الحالات ، المترجم في الواقع بحاجة إلى معرفة حجم التخزين في تلك الحالات.نظرا لحجم التخزين تنفيذ محددة ، العديد من المجمعين قد تختار استخدام 32 بت القيم الأساسية نوع من كل التعداد ، وهي النقطة التي يصبح من الممكن قدما يعلن لهم.تجربة مثيرة للاهتمام قد تكون محاولة إلى الأمام معلنا إجراء التعداد في visual studio ثم إجبارها على استخدام الكامنة نوع أكبر من sizeof(int) كما هو موضح أعلاه لنرى ماذا سيحدث.

عن VC, هذا الاختبار عن الأمام الإعلان وتحديد الأساسية نوع:

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

ولكن حصلت على تحذير /م4(/W3 لا تتحمل هذا التحذير)

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

  1. VC(Microsoft (R) 32-بت C/C++ تحسين مترجم نسخة 15.00.30729.01 عن أجهزة 80 x 86) يبدو عربات التي تجرها الدواب في الحالة المذكورة أعلاه:

    • عند رؤية التعداد 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 ونقل تعريف وظيفة فو بعد التعداد T التعريف:والنتيجة هي موافق:

الرئيسية المذكورة أعلاه تعليمات يصبح:

mov BYTE PTR [eax], 120 ;00000078H

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

ملاحظة قيمة لا يتم الكتابة

وذلك باستخدام من الأمام-إعلان التعداد في VC تعتبر ضارة.

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

في مشاريعي ، لقد اعتمدت مساحة-لا بد التعداد تقنية التعامل مع enums من تراث و 3rd الطرف المكونات.هنا مثال:

إلى الأمام.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

فو.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

علما بأن foo.h رأس لا أعرف أي شيء عن legacy::evil.فقط الملفات التي تستخدم إرث نوع legacy::evil (هنا:main.cc) يجب أن تشمل enum.h.

الحل لمشكلتك قد تكون إما:

1 - استخدم الباحث بدلا من enums:تعلن الخاص بك رجات في مجهول الاسم في الملف 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 التجسيدات مثل ذلك في جافا.إلى الأمام تعلن الصف ، ثم تعريفها في الملف CPP ، instanciate فقط enum مثل القيم.لقد فعلت شيئا مثل هذا في C++ و النتيجة لم تكن مرضية كما المطلوب, كما انها بحاجة الى بعض التعليمات البرمجية لمحاكاة وقوع enum (نسخ البناء, مشغل = ، إلخ.).

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

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

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

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

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

عندما قدما معلنا حقيقة أن كنت سوف تستخدم 'محدودة' في وقت لاحق لا تضيف أي قيمة:اللاحقة رمز يحتاج إلى معرفة القيم الممكنة من أجل الاستفادة من ذلك.

على الرغم من أن المترجم هو تشعر بالقلق إزاء حجم نوع المذكورة ، القصد من تعداد يضيع عند توجيه نعلن ذلك.

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