C ++ 'da statik bir sınıf nasıl oluşturulur?


263

C ++ 'da statik bir sınıf nasıl oluşturulur? Gibi bir şey yapabilmeliyim:

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

BitParserSınıfı oluşturduğumu varsayarsak . BitParserSınıf tanımı nasıl olurdu ?


198
Evet. Eski günlerde, biz buna sadece "işlev" adını verdik. Bugün çılgın "isim alanlarınız" ile çocuklar. Hey, çimlerimden çekil! <yumruk sallıyor>
Vagrant

7
@Ad alanı içindeki bir işlev hala bir işlevdir. Bir sınıfa ait bir işleve yöntem denir. Statik bir yöntemse, bunu bir ad alanının içindeki bir işlevmiş gibi çağırırsınız.

48
5 yıl sonra şimdi @Vagrant ile beraberim. Ne saçma bir soru!
andrewrk

16
9 yıl sonra ve şimdi sınıfları bile olmayan kendi programlama dilim var: ziglang.org
andrewrk

1
IMO kapsayıcı benzeri sınıflar (yalnızca statik yöntemlere sahip) bazı durumlarda yararlıdır.
AarCee

Yanıtlar:


272

Örneğin "C #" gibi bir sınıfa "statik" anahtar kelimesini uygulamanın bir yolunu arıyorsanız, Yönetilen C ++ kullanmadan bunu yapamazsınız.

Ancak örneğinizin görünümünde, BitParser nesneniz üzerinde genel bir statik yöntem oluşturmanız yeterlidir. Şöyle ki:

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;
}

Bu kodu, yöntemi örnek kodunuzla aynı şekilde çağırmak için kullanabilirsiniz.

Umarım yardımcı olur! Şerefe.


2
OJ, bir sözdizimi hatası var . Statik anahtar kelime, yöntem tanımında değil, yalnızca sınıf tanımında kullanılmalıdır.
andrewrk

90
Bu yaklaşımda niyetinizi netleştirmek için ayrıca özel bir kurucu kullanabilirsiniz. private: BitParser() {}Bu, herhangi birinin örnek oluşturmasını engelleyecektir.
Danvil

7
@MoatazElmasry iş parçacığı güvenliği, durumu paylaştığınızda bir sorundur. Yukarıdaki uygulamada paylaşılan bir durum yoktur, bu nedenle iş parçacığı güvenliği ile ilgili herhangi bir sorun olamaz ... bu işlevler içinde statik kullanmak için yeterince aptal değilseniz . Yani evet, yukarıdaki kod iş parçacığı güvenli, sadece kalıcı durumu fonksiyonlarınızdan uzak tutun ve iyisiniz.
OJ.

@MoatazElmasry Yanlış. İki iş parçacığı, statik bir işlevde statik olmayan yerel değişkenleri değiştiremez.
OJ.

12
C ++ 11 ise, BitParser() = delete;yapıcıyı kaldırma niyetini doğru bir şekilde iletmenin daha iyi olduğunu iddia ederim (sadece gizlemek değil private).
phoenix

247

Matt Price'ın çözümünü düşünün .

  1. C ++ 'da "statik sınıf" ın bir anlamı yoktur. En yakın şey, sadece statik yöntemlere ve üyelere sahip bir sınıftır.
  2. Statik yöntemler kullanmak yalnızca sizi sınırlar.

İstediğin (o için işlevini koymak, C ++ semantik olarak ifade olduğunu ise bir ad alanında bir işlev).

Düzenle 2011-11-11

C ++ 'da "statik sınıf" yoktur. En yakın kavram sadece statik yöntemlere sahip bir sınıf olacaktır. Örneğin:

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

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

Ancak "statik sınıfların", üye olmayan işlevlere sahip olamayan Java benzeri dillerde (örn. C #) bilgisayar korsanları olduğunu hatırlamak zorundasınız; bu nedenle, bunları statik yöntemler olarak sınıfların içine taşımak zorundalar.

C ++ 'da, gerçekten istediğiniz bir ad alanında bildireceğiniz üye olmayan bir işlevdir:

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

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

Neden?

C ++ 'da, ad alanı "Java statik yöntem" deseni için sınıflardan daha güçlüdür, çünkü:

  • statik yöntemler sınıflara özel sembollere erişebilir
  • özel statik yöntemler hala herkes tarafından görülebilir (erişilemiyorsa), bu da kapsüllemeyi biraz ihlal eder
  • statik yöntemler ileri bildirilemez
  • statik yöntem kitaplık başlığını değiştirmeden sınıf kullanıcısı tarafından aşırı yüklenemez
  • aynı ad alanında (muhtemelen arkadaş) üye olmayan bir işlevden daha iyi yapılamayan statik bir yöntemle yapılabilecek hiçbir şey yoktur
  • ad alanlarının kendi semantikleri vardır (birleştirilebilir, anonim olabilir, vb.)
  • vb.

Sonuç: Java / C # 'ın desenini C ++' da kopyalamayın / yapıştırmayın. Java / C # 'da desen zorunludur. Ancak C ++ 'da kötü bir stil.

Düzenle 2010-06-10

Statik yöntemin lehine bir argüman vardı, çünkü bazen statik özel üye değişkeni kullanmak gerekiyor.

Aşağıda gösterildiği gibi biraz katılmıyorum:

"Statik özel üye" çözümü

// HPP

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

İlk olarak, myGlobal'a myGlobal denir, çünkü hala küresel bir özel değişkentir. CPP kaynağına bir bakış aşağıdakileri açıklığa kavuşturacaktır:

// 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 !!!
}

İlk bakışta, serbest fonksiyon barC'nin Foo :: myGlobal'a erişememesi bir kapsülleme bakış açısından iyi bir şey gibi görünüyor ... Çok iyi, çünkü HPP'ye bakan biri (sabotaj başvurmadan) erişemez foo :: myGlobal.

Ancak yakından bakarsanız, bunun muazzam bir hata olduğunu göreceksiniz: HPP'de sadece özel değişkeniniz hala ilan edilmemeli (ve özel olmasına rağmen tüm dünya tarafından görülebilir), ancak beyan etmelisiniz aynı HPP'de erişmeye yetkili tüm (TÜMÜNDEKİ gibi) işlevler !!!

Yani özel bir statik üye kullanmak, cildinize dövme yapan sevgililerinizin listesi ile çıplak bir şekilde dışarıda yürümek gibidir: Kimse dokunmaya yetkilidir, ancak herkes göz atabilir. Ve bonus: Herkes, kendi özelliğinizle oynama yetkisi olanların isimlerine sahip olabilir.

private gerçekten ... :-D

"Anonim ad alanları" çözümü

Anonim ad alanları, işleri gerçekten özel yapma avantajına sahip olacaktır.

İlk olarak, HPP başlığı

// HPP

namespace Foo
{
   void barA() ;
}

Sadece belirttiğinizden emin olmak için: barB veya myGlobal'ın işe yaramaz bir açıklaması yoktur. Bu, başlığı okuyan hiç kimsenin barA'nın arkasında neyin gizlendiğini bilmediği anlamına gelir.

Ardından, CPP:

// 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 !!!
}

Gördüğünüz gibi, "statik sınıf" bildirimi gibi, fooA ve fooB hala myGlobal'a erişebiliyor. Ama başka hiç kimse yapamaz. Ve bu CPP'nin dışında hiç kimse fooB ve myGlobal'ın bile var olduğunu bilmiyor!

Derisinde dövülmüş adres defteri ile çıplak yürüyen "statik sınıf" ın aksine "anonim" ad alanı tamamen giyinmiş , bu AFAIK çok daha iyi kapsüllenmiş görünüyor.

Gerçekten önemli mi?

Kodunuzun kullanıcılar sabotörler sürece, ne (ben bir egzersiz olarak, bir ... kirli bir davranış-tanımlanmamış kesmek kullanarak bir kamu sınıfın özel kısmına nasıl erişebileceğini bulmak, size haber vereceğiz) privateise private, hatta eğer privatebir sınıfın başlıkta bildirilen bölümünde görünür .

: Eğer özel üye erişimi olan başka bir "özel işlev" eklemeniz gerekiyorsa Yine, hala ben endişeliyim kadarıyla bir paradoks başlık değiştirerek tüm dünyaya ilan etmeli Ben uygulanmasını değiştirirseniz kodum (CPP kısmı), daha sonra arayüz (HPP kısmı) değişmemelidir. Leonidas'dan alıntı: " Bu KAHRAMAN! "

Düzenle 2014-09-20

Sınıflar statik yöntemler ne zaman üye olmayan işlevlere sahip ad alanlarından daha iyidir?

İşlevleri gruplandırmanız ve bu grubu bir şablona beslemeniz gerektiğinde:

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 !!!

Çünkü, sınıf şablon parametresi olabiliyorsa, ad alanları olamaz.


3
GCC, özel sınıf üyelerine erişmek için beyaz kutu birim testlerinde kullanılabilen -fno-access-control'ü destekler. Bu, uygulamada anonim / statik bir küresel yerine bir sınıf üyesi kullanmayı haklı kılmamın tek nedeni.
Tom

8
@Tom: Platformlar arası bir çözüm #define private publicbaşlıklara aşağıdaki kodu eklemek olacaktır ... ^ _ ^ ...
paercebal

1
@Tom: Neyse, IMHO, hatta birim testler düşünüldüğünde bile, "çok fazla şey gösterme" yönündeki eksilerini artıları ağır basar. Alternatif bir çözüm, bir utilitiesisim alanında gerekli parametreleri (ve daha fazla) alarak bir fonksiyonda test edilecek kodu koymak olacağını tahmin ediyorum . Bu şekilde, bu işlev birim test edilebilir ve özel üyelere hala özel erişimi yoktur (işlev çağrısında parametre olarak verildiği gibi) ...
paercebal

@paercebal Geminize atlamak üzereyim ama son bir rezervasyonum var. Birisi sizin namespaceisteğinize globalatlarsa, gizli de olsa üyelerinize erişemez mi? Açıkçası tahmin etmek zorunda kalacaklar, ancak kodunuzu kasıtlı olarak gizlemiyorsanız, değişken adlarını tahmin etmek oldukça kolaydır.
Zak

@Zak: Gerçekten de yapabilirler, ancak bunu sadece myGlobal değişkeninin bildirildiği CPP dosyasında yapmaya çalışarak. Mesele erişilebilirlikten daha fazla görünürlüktür. Statik sınıfta, myGlobal değişkeni özeldir, ancak yine de görülebilir. Bu göründüğü kadar önemli değil, ancak yine de, bir DLL'de, dışa aktarılan bir başlıktaki DLL'ye özel olması gereken bir sembolü gösteren garip olabilir ... Ad alanında, myGlobal yalnızca CPP dosyasında (siz daha da ileri gidebilir ve statik hale getirebilir). Bu değişken genel başlıklarda görünmez.
paercebal

63

Ayrıca bir ad alanında ücretsiz bir işlev de oluşturabilirsiniz:

BitParser.h içinde

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

BitParser.cpp içinde

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

Genel olarak bu, kod yazmanın tercih edilen yolu olacaktır. Bir nesneye gerek olmadığında sınıf kullanmayın.


1
Bazı durumlarda, sınıf çoğunlukla "statik" olsa bile veri kapsülleme yapmak isteyebilirsiniz. Statik özel sınıf üyeleri size bunu verecektir. Ad alanı üyeleri her zaman herkese açıktır ve veri kapsüllemesi sağlayamazlar.
Torleif

"Member" var yalnızca .cpp dosyasından bildirilir ve erişilirse, .h dosyasında bildirilen özel bir vardan daha özeldir. Bu tekniği tavsiye ETMEM.
jmucchiello

3
@Torleif: Yanılıyorsun. isim alanları kapsülleme için statik özel üyelere göre daha iyidir. Gösteri için cevabımı görün.
paercebal

1
evet ancak ad alanında, statik üyeli sınıfın aksine işlev sırasını korumalısınız, örneğin void a () {b ();} b () {}, bir ad alanında hata verir, ancak statik üyeler
Moataz Elmasry

13

Örneğin "C #" gibi bir sınıfa "statik" anahtar kelimesini uygulamanın bir yolunu arıyorsanız

statik sınıflar sadece sizi tutan ve herhangi bir örnek yöntem / değişken yazmanızı engelleyen derleyicidir.

Herhangi bir örnek yöntemi / değişkeni olmadan normal bir sınıf yazarsanız, aynı şeydir ve C ++ ile yapacağınız şey budur


Şikayet etmemek (özellikle size), ancak kelimeyi static200 kez yazmaktan veya kesmek / yapıştırmaktan korumak için bazı derleyici el tutma iyi bir şey olacaktır.
3Dave

Kabul edildi - ancak C # 'daki statik bir sınıf bunu da yapmaz. Oraya statik yapıştırmayı unuttuğunuzda derlenemiyor :-)
Orion Edwards

Evet - yeterince adil. Makrolarım gösteriliyor. Dürüst olmak gerekirse, sınıfı statik olarak ilan edersem, derleyici sadece onu başlatmaya çalışırsam bir hata atmalıdır. Kendimi tekrarlamamı gerektiren kurallar iğrençtir ve devrim geldiğinde duvara karşı ilk olmalıdır.
3Dave

11

C ++ 'da bir sınıfın statik bir işlevi (statik bir sınıf değil) oluşturmak istiyorsunuz.

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

Daha sonra, istenen sonuç olduğunu düşündüğüm bir nesneyi örneklemeden BitParser :: getBitAt () işlevini kullanarak işlevi çağırabilirsiniz.


11

Gibi bir şey yazabilir miyim static class?

Hayır , C ++ 11 N3337 standart taslağı Ek C 7.1.1'e göre :

Değişiklik: C ++ 'da, statik veya extern belirteçleri yalnızca nesne veya işlevlerin adlarına uygulanabilir. Bu tanımlayıcıları tür bildirimleriyle kullanmak C ++ 'da yasa dışıdır. C dilinde, tip tanımlarında kullanıldığında bu belirticiler yok sayılır. Misal:

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

Gerekçe: Depolama sınıfı belirteçlerinin bir türle ilişkilendirildiğinde hiçbir anlamı yoktur. C ++ 'da, sınıf üyeleri statik depolama sınıfı belirleyicisi ile bildirilebilir. Tür bildirimlerinde depolama sınıfı belirteçlerine izin vermek, kodu kullanıcılar için kafa karıştırıcı hale getirebilir.

Ve benzeri struct, classaynı zamanda bir tür bildirimi olduğunu.

Aynısı Ek A'daki sözdizimi ağacının yürüyüşüyle ​​çıkarılabilir.

static structC'de yasal olduğunu , ancak hiçbir etkisi olmadığını belirtmek ilginçtir : C programlamasında neden ve ne zaman statik yapılar kullanılır?


6

Burada belirtildiği gibi, bunu C ++ ile başarmanın daha iyi bir yolu ad alanlarını kullanmak olabilir. Ancak kimse finalburada anahtar kelimeden bahsetmediği için static class, C # 'dan doğrudan bir eşdeğerinin C ++ 11 veya sonraki sürümlerinde nasıl görüneceğini gönderiyorum :

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

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

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

5

C ++ 'da statik bir sınıfa sahip olabilirsiniz, daha önce de belirtildiği gibi, statik sınıf, herhangi bir nesnesi olmayan bir sınıftır. C ++ 'da bu, yapıcı / yıkıcı özel olarak bildirilerek elde edilebilir. Sonuç aynı.


Önerdiğiniz şey tek bir Sınıf oluşturabilir, ancak statik sınıfla aynı değildir.
ksinkar

4

Yönetilen C ++ 'da statik sınıf sözdizimi: -

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

... geç olsun güç olmasın...


3

Bu, C # 'ın C ++' da yapma şekline benzer

C # file.cs dosyasında ortak bir işlevin içinde özel değişkene sahip olabilirsiniz. Başka bir dosyadayken, aşağıdaki gibi işlevli ad alanını çağırarak kullanabilirsiniz:

MyNamespace.Function(blah);

Aynı şey C ++ 'da nasıl yapılır:

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
  }
}

OtherFile.h

#include "SharedModule.h"

OtherFile.cpp

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

2
Ama herkes TheDataToBeHidden'a gidebilir -> Bu bir çözüm değil
Guy L

3

Diğer yönetilen programlama dillerinden farklı olarak, "statik sınıf" ın C ++ ile anlamı yoktur. Statik üye işlevini kullanabilirsiniz.


0

Ad alanlarının "statik sınıflar" elde etmek için çok yararlı olmadığı bir durum, bu sınıfları kalıtım üzerinde kompozisyon elde etmek için kullanmaktır. Ad alanları sınıf arkadaşları olamaz ve bu nedenle sınıfın özel üyelerine erişemez.

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;
  }
};

0

Bir (birçok) alternatif, ancak en çok (bence) zarif (statik davranışları taklit etmek için ad alanlarını ve özel kurucuları kullanma ile karşılaştırıldığında), C ++ 'da "somutlaştırılamayan sınıf" davranışını elde etmenin yolu privateerişim değiştirici ile kukla saf sanal işlev bildirir.

class Foo {
   public:
     static int someMethod(int someArg);

   private:
     virtual void __dummy() = 0;
};

C ++ 11 kullanıyorsanız, sınıf tanımlamasındaki finalbelirteci kullanarak diğer sınıfların miras almasını kısıtlamak için sınıfın miras alınmadığından (yalnızca statik bir sınıfın davranışını taklit etmek için) fazladan yol kat edebilirsiniz. .

// C++11 ONLY
class Foo final {
   public:
     static int someMethod(int someArg);

   private:
      virtual void __dummy() = 0;
};

Göründüğü kadar aptalca ve mantıksız, C ++ 11, geçersiz kılınamayan sınıfın finalstatik davranışı tamamen ve tamamen uygulamak için bildirmenin yanı sıra, geçersiz kılınamayan "saf sanal işlev" bildirilmesine izin verir hiçbir şekilde kalıtsal olmamak ve kukla işlev geçersiz kılmamak.

// C++11 ONLY
class Foo final {
   public:
     static int someMethod(int someArg);

   private:
     // Other private declarations

     virtual void __dummy() = 0 final;
}; // Foo now exhibits all the properties of a static class
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.