سؤال

كيف يمكنك إنشاء فئة ثابتة في C++؟يجب أن أكون قادرًا على القيام بشيء مثل:

cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;

على افتراض أنني خلقت BitParser فصل.ماذا سيكون BitParser تعريف الطبقة تبدو وكأنها؟

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

المحلول

إذا كنت تبحث عن طريقة لتطبيق الكلمة الأساسية "الثابتة" على الفصل الدراسي، مثلما تفعل في C# على سبيل المثال، فلن تتمكن من القيام بذلك دون استخدام Managed C++.

ولكن يبدو أن العينة الخاصة بك تحتاج فقط إلى إنشاء طريقة ثابتة عامة على كائن BitParser الخاص بك.مثل ذلك:

BitParser.h

class BitParser
{
 public:
  static bool getBitAt(int buffer, int bitIndex);

  // ...lots of great stuff

 private:
  // Disallow creating an instance of this object
  BitParser() {}
};

BitParser.cpp

bool BitParser::getBitAt(int buffer, int bitIndex)
{
  bool isBitSet = false;
  // .. determine if bit is set
  return isBitSet;
}

يمكنك استخدام هذا الرمز لاستدعاء الطريقة بنفس طريقة رمز المثال الخاص بك.

امل ان يساعد!هتافات.

نصائح أخرى

يعتبر حل مات برايس.

  1. في C++، "الفئة الثابتة" ليس لها أي معنى.أقرب شيء هو فئة تحتوي على أساليب وأعضاء ثابتين فقط.
  2. إن استخدام الأساليب الثابتة لن يؤدي إلا إلى تقييدك.

ما تريده هو، معبرًا عنه في دلالات C++، أن تضع وظيفتك (لها يكون دالة) في مساحة الاسم.

تحرير 2011-11-11

لا توجد "فئة ثابتة" في C++.سيكون المفهوم الأقرب هو فئة ذات طرق ثابتة فقط.على سبيل المثال:

// header
class MyClass
{
   public :
      static void myMethod() ;
} ;

// source
void MyClass::myMethod()
{
   // etc.
}

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

في C++، ما تريده حقًا هو وظيفة غير عضو ستعلن عنها في مساحة الاسم:

// header
namespace MyNamespace
{
   void myMethod() ;
}

// source
namespace MyNamespace
{
   void myMethod()
   {
      // etc.
   }
}

لماذا هذا؟

في C++، تكون مساحة الاسم أقوى من الفئات الخاصة بنمط "طريقة Java الثابتة"، للأسباب التالية:

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

خاتمة:لا تقم بنسخ/لصق نمط Java/C# في C++.في Java/C#، النمط إلزامي.ولكن في لغة C++، هذا أسلوب سيء.

تحرير 2010-06-10

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

أنا لا أتفق إلى حد ما، كما هو مبين أدناه:

الحل "العضو الخاص الثابت".

// HPP

class Foo
{
   public :
      void barA() ;
   private :
      void barB() ;
      static std::string myGlobal ;
} ;

أولاً، يُسمى myGlobal myGlobal لأنه لا يزال متغيرًا عالميًا خاصًا.ستوضح نظرة على مصدر CPP ما يلي:

// CPP
std::string Foo::myGlobal ; // You MUST declare it in a CPP

void Foo::barA()
{
   // I can access Foo::myGlobal
}

void Foo::barB()
{
   // I can access Foo::myGlobal, too
}

void barC()
{
   // I CAN'T access Foo::myGlobal !!!
}

للوهلة الأولى، تبدو حقيقة أن الوظيفة المجانية barC لا يمكنها الوصول إلى Foo::myGlobal أمرًا جيدًا من وجهة نظر التغليف...إنه أمر رائع لأن أي شخص ينظر إلى HPP لن يتمكن (ما لم يلجأ إلى التخريب) من الوصول إلى Foo::myGlobal.

ولكن إذا نظرت إليه عن كثب ستجد أنه خطأ فادح:ليس فقط المتغير الخاص الخاص بك يجب أن يتم التصريح عنه في HPP (وبالتالي، يكون مرئيًا لكل العالم، على الرغم من كونه خاصًا)، ولكن يجب عليك التصريح في نفس HPP عن جميع الوظائف (كما هو الحال في ALL) التي سيتم السماح لها بالوصول إليها! !!

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

private بالفعل...:-د

الحل "مساحات الأسماء المجهولة".

تتمتع مساحات الأسماء المجهولة بميزة جعل الأشياء خاصة حقًا.

أولاً، رأس HPP

// HPP

namespace Foo
{
   void barA() ;
}

فقط للتأكد من أنك لاحظت:لا يوجد إعلان عديم الفائدة عن barB أو myGlobal.مما يعني أنه لا أحد يقرأ الرأس يعرف ما هو مخفي وراء البارا.

ثم حزب المؤتمر الشعبي:

// CPP
namespace Foo
{
   namespace
   {
      std::string myGlobal ;

      void Foo::barB()
      {
         // I can access Foo::myGlobal
      }
   }

   void barA()
   {
      // I can access myGlobal, too
   }
}

void barC()
{
   // I STILL CAN'T access myGlobal !!!
}

كما ترون، مثل ما يسمى بإعلان "الفئة الثابتة"، لا يزال بإمكان fooA وfooB الوصول إلى myGlobal.لكن لا أحد يستطيع ذلك.ولا أحد خارج CPP يعرف بوجود fooB وmyGlobal!

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

هل هو مهم حقا؟

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

ومع ذلك، إذا كنت بحاجة إلى إضافة "وظيفة خاصة" أخرى مع إمكانية الوصول إلى العضو الخاص، فلا يزال يتعين عليك الإعلان عنها للعالم أجمع عن طريق تعديل الرأس، وهو ما يمثل مفارقة بالنسبة لي: إذا قمت بتغيير تنفيذ الكود الخاص بي (جزء CPP)، فيجب ألا تتغير الواجهة (جزء HPP). نقلاً عن ليونيداس:"هذا هو التغليف!"

تحرير 2014-09-20

متى تكون الأساليب الثابتة للفئات في الواقع أفضل من مساحات الأسماء ذات الوظائف غير الأعضاء؟

عندما تحتاج إلى تجميع الوظائف معًا وإدخال هذه المجموعة في قالب:

namespace alpha
{
   void foo() ;
   void bar() ;
}

struct Beta
{
   static void foo() ;
   static void bar() ;
};

template <typename T>
struct Gamma
{
   void foobar()
   {
      T::foo() ;
      T::bar() ;
   }
};

Gamma<alpha> ga ; // compilation error
Gamma<Beta> gb ;  // ok
gb.foobar() ;     // ok !!!

لأنه، إذا كان من الممكن أن تكون الفئة معلمة قالب، فلا يمكن أن تكون مساحات الأسماء.

يمكنك أيضًا إنشاء دالة مجانية في مساحة الاسم:

في BitParser.h

namespace BitParser
{
    bool getBitAt(int buffer, int bitIndex);
}

في BitParser.cpp

namespace BitParser
{
    bool getBitAt(int buffer, int bitIndex)
    {
        //get the bit :)
    }
}

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

إذا كنت تبحث عن طريقة لتطبيق الكلمة الأساسية "الثابتة" على الفصل الدراسي، كما يمكنك في C# على سبيل المثال

الفئات الثابتة هي مجرد المترجم الذي يمسك بك ويمنعك من كتابة أي أساليب/متغيرات مثيل.

إذا كتبت فئة عادية دون أي أساليب/متغيرات مثيل، فهذا هو نفس الشيء، وهذا ما ستفعله في C++

في C++ تريد إنشاء وظيفة ثابتة لفئة (وليس فئة ثابتة).

class BitParser {
public:
  ...
  static ... getBitAt(...) {
  }
};

يجب أن تكون قادرًا بعد ذلك على استدعاء الوظيفة باستخدام BitParser::getBitAt() دون إنشاء كائن أفترض أنه النتيجة المرجوة.

هل يمكنني كتابة شيء مثل static class?

لا, ، بحسب ال المسودة القياسية C++11 N3337 الملحق ج 7.1.1:

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

static struct S {    // valid C, invalid in C++
  int i;
};

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

ومثل struct, class هو أيضًا إعلان نوع.

ويمكن استنتاج الشيء نفسه من خلال السير على شجرة بناء الجملة في الملحق أ.

ومن المثير للاهتمام أن نلاحظ ذلك static struct كان قانونيًا في لغة C، لكن لم يكن له أي تأثير: لماذا ومتى يتم استخدام الهياكل الثابتة في برمجة C؟

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

في Managed C++، بناء جملة الفئة الثابتة هو: -

public ref class BitParser abstract sealed
{
    public:
        static bool GetBitAt(...)
        {
            ...
        }
}

...أن تأتي متأخرا أفضل من ألا تأتي أبدا...

وهذا مشابه لطريقة C# للقيام بذلك في C++

في ملف C# file.cs، يمكنك الحصول على متغير خاص داخل وظيفة عامة.عندما تكون في ملف آخر، يمكنك استخدامه عن طريق استدعاء مساحة الاسم باستخدام الوظيفة كما في:

MyNamespace.Function(blah);

إليك كيفية تطبيق نفس الشيء في C++:

SharedModule.h

class TheDataToBeHidden
{
  public:
    static int _var1;
    static int _var2;
};

namespace SharedData
{
  void SetError(const char *Message, const char *Title);
  void DisplayError(void);
}

SharedModule.cpp

//Init the data (Link error if not done)
int TheDataToBeHidden::_var1 = 0;
int TheDataToBeHidden::_var2 = 0;


//Implement the namespace
namespace SharedData
{
  void SetError(const char *Message, const char *Title)
  {
    //blah using TheDataToBeHidden::_var1, etc
  }

  void DisplayError(void)
  {
    //blah
  }
}

ملف آخر.h

#include "SharedModule.h"

ملف آخر.cpp

//Call the functions using the hidden variables
SharedData::SetError("Hello", "World");
SharedData::DisplayError();

على عكس لغات البرمجة المُدارة الأخرى، فإن "الفئة الثابتة" ليس لها أي معنى في لغة C++.يمكنك الاستفادة من وظيفة العضو الثابت.

كما تمت الإشارة هنا، قد تكون الطريقة الأفضل لتحقيق ذلك في لغة C++ هي استخدام مساحات الأسماء.ولكن بما أن أحدا لم يذكر final الكلمة الرئيسية هنا، أقوم بنشر ما يعادل مباشرة static class من C# سيبدو كما هو الحال في C++ 11 أو الأحدث:

class BitParser final
{
public:
  BitParser() = delete;

  static bool GetBitAt(int buffer, int pos);
};

bool BitParser::GetBitAt(int buffer, int pos)
{
  // your code
}

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

class Class {
 public:
  void foo() { Static::bar(*this); }    

 private:
  int member{0};
  friend class Static;
};    

class Static {
 public:
  template <typename T>
  static void bar(T& t) {
    t.member = 1;
  }
};
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top