Jak stworzyć klasę statyczną w C ++? Powinienem być w stanie zrobić coś takiego:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Zakładając, że stworzyłem BitParser
klasę. Jak wyglądałaby BitParser
definicja klasy?
Jak stworzyć klasę statyczną w C ++? Powinienem być w stanie zrobić coś takiego:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Zakładając, że stworzyłem BitParser
klasę. Jak wyglądałaby BitParser
definicja klasy?
Odpowiedzi:
Jeśli szukasz sposobu zastosowania słowa kluczowego „static” do klasy, jak na przykład w C #, nie będziesz w stanie tego zrobić bez użycia Managed C ++.
Ale wygląd próbki wystarczy utworzyć publiczną metodę statyczną na obiekcie BitParser. Tak jak:
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;
}
Możesz użyć tego kodu, aby wywołać metodę w taki sam sposób, jak kod przykładowy.
Mam nadzieję, że to pomaga! Twoje zdrowie.
private: BitParser() {}
Uniemożliwi to każdemu tworzenie instancji.
BitParser() = delete;
odpowiednio przekazać zamiar usunięcia konstruktora (nie tylko ukrywanie go jako private
).
Zastanów się nad rozwiązaniem Matta Price'a .
To, czego chcesz, to wyrażone w semantyce C ++, aby umieścić swoją funkcję (ponieważ jest to funkcja) w przestrzeni nazw.
W C ++ nie ma „klasy statycznej”. Najbliższą koncepcją byłaby klasa posiadająca wyłącznie metody statyczne. Na przykład:
// header
class MyClass
{
public :
static void myMethod() ;
} ;
// source
void MyClass::myMethod()
{
// etc.
}
Musisz jednak pamiętać, że „klasy statyczne” to włamania w językach podobnych do Javy (np. C #), które nie mogą mieć funkcji nie będących członkami, więc zamiast tego muszą przenieść je do klas jako metody statyczne.
W C ++ tak naprawdę chcesz funkcji nieosobistej, którą zadeklarujesz w przestrzeni nazw:
// header
namespace MyNamespace
{
void myMethod() ;
}
// source
namespace MyNamespace
{
void myMethod()
{
// etc.
}
}
W C ++ przestrzeń nazw ma większą moc niż klasy dla wzorca „Java static method”, ponieważ:
Wniosek: Nie kopiuj / wklej tego wzorca Java / C # w C ++. W Javie / C # wzorzec jest obowiązkowy. Ale w C ++ jest to zły styl.
Argumentowano za metodą statyczną, ponieważ czasami trzeba użyć statycznej zmiennej członka prywatnego.
Nie zgadzam się, jak pokazano poniżej:
// HPP
class Foo
{
public :
void barA() ;
private :
void barB() ;
static std::string myGlobal ;
} ;
Po pierwsze, myGlobal nazywa się myGlobal, ponieważ wciąż jest globalną zmienną prywatną. Spojrzenie na źródło CPP wyjaśni, że:
// 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 !!!
}
Na pierwszy rzut oka fakt, że bezpłatna funkcja barC nie może uzyskać dostępu do Foo :: myGlobal, wydaje się dobrą rzeczą z punktu widzenia enkapsulacji ... Fajnie, ponieważ ktoś patrząc na HPP nie będzie w stanie (chyba, że ucieknie się do sabotażu) uzyskać dostęp Foo :: myGlobal.
Ale jeśli przyjrzysz się temu uważnie, okaże się, że jest to kolosalny błąd: nie tylko twoja prywatna zmienna musi być nadal zadeklarowana w HPP (a więc widoczna dla całego świata, mimo że jest prywatna), ale musisz zadeklarować w tym samym HPP wszystkie (jak we WSZYSTKICH) funkcjach, które będą uprawnione do dostępu do niego !!!
Zatem używanie prywatnego statycznego członka jest jak chodzenie nago na zewnątrz z listą kochanków wytatuowaną na skórze: Nikt nie jest upoważniony do dotykania, ale każdy jest w stanie zerknąć. I bonus: każdy może mieć nazwiska osób upoważnionych do korzystania z twoich uprawnień.
private
rzeczywiście ... :-D
Anonimowe przestrzenie nazw będą miały tę zaletę, że uczynią rzeczy prywatnymi naprawdę prywatnymi.
Najpierw nagłówek HPP
// HPP
namespace Foo
{
void barA() ;
}
Dla pewności, że zauważyłeś: Nie ma bezużytecznej deklaracji barB ani myGlobal. Co oznacza, że nikt nie czytający nagłówka nie wie, co kryje się za paskiem A.
Następnie 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 !!!
}
Jak widać, podobnie jak tak zwana deklaracja „klasy statycznej”, fooA i fooB nadal mają dostęp do myGlobal. Ale nikt inny nie może. I nikt poza CPP nie wie, że fooB i myGlobal nawet istnieją!
W przeciwieństwie do „klasy statycznej” chodzącej nago z książką adresową wytatuowaną na skórze, „anonimowa” przestrzeń nazw jest w pełni ubrana , co wydaje się znacznie lepiej zamknięte AFAIK.
O ile użytkownicy Twojego kodu nie są sabotażystami (pozwolę ci, jako ćwiczenie, dowiedzieć się, jak można uzyskać dostęp do prywatnej części klasy publicznej za pomocą brudnego, niezdefiniowanego zachowania hack ...), co to private
jest private
, nawet jeśli to jest widoczny w private
sekcji klasy zadeklarowanej w nagłówku.
Jeśli jednak chcesz dodać kolejną „funkcję prywatną” z dostępem do prywatnego członka, nadal musisz ją zadeklarować na całym świecie, modyfikując nagłówek, co jest moim zdaniem paradoksem: jeśli zmienię implementację mój kod (część CPP), a następnie interfejs (część HPP) NIE powinien się zmienić. Cytując Leonidasa: „ To jest ENCAPSULATION! ”
Kiedy klasy metody statyczne są w rzeczywistości lepsze niż przestrzenie nazw z funkcjami nieczłonkowskimi?
Gdy musisz zgrupować funkcje i nakarmić tę grupę szablonem:
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 !!!
Ponieważ jeśli klasa może być parametrem szablonu, przestrzenie nazw nie mogą.
#define private public
w nagłówkach ... ^ _ ^ ...
utilities
przestrzeni nazw. W ten sposób, ta funkcja może być jednostka sprawdzone i nadal nie mają specjalnego dostępu do członków prywatnych (jak są one podane jako parametry w wywołaniu funkcji) ...
namespace
wolę, nie uzyska dostępu do twoich global
, choć ukrytych, członków? Oczywiście musieliby zgadywać, ale jeśli celowo nie zaciemniacie swojego kodu, nazwy zmiennych są dość łatwe do odgadnięcia.
Możesz także utworzyć bezpłatną funkcję w przestrzeni nazw:
W BitParser.h
namespace BitParser
{
bool getBitAt(int buffer, int bitIndex);
}
W BitParser.cpp
namespace BitParser
{
bool getBitAt(int buffer, int bitIndex)
{
//get the bit :)
}
}
Zasadniczo byłby to preferowany sposób pisania kodu. Gdy obiekt nie jest potrzebny, nie używaj klasy.
Jeśli szukasz sposobu zastosowania słowa kluczowego „static” do klasy, na przykład w języku C #
klasy statyczne są po prostu kompilatorem, który trzyma cię za rękę i powstrzymuje cię przed pisaniem metod / zmiennych instancji.
Jeśli po prostu napiszesz normalną klasę bez żadnych metod / zmiennych instancji, to jest to samo i tak właśnie zrobiłbyś w C ++
static
dobrze byłoby trochę trzymać kompilatora za rękę, aby powstrzymać mnie od pisania lub wycinania / wklejania słowa 200 razy.
W C ++ chcesz utworzyć funkcję statyczną klasy (nie klasę statyczną).
class BitParser {
public:
...
static ... getBitAt(...) {
}
};
Powinieneś być w stanie wywołać funkcję przy użyciu BitParser :: getBitAt () bez tworzenia obiektu, który, jak zakładam, jest pożądanym rezultatem.
Czy mogę napisać coś takiego static class
?
Nie , zgodnie ze standardowym projektem C ++ 11 N3337, załącznik C 7.1.1:
Zmiana: W C ++ specyfikatory statyczne lub zewnętrzne mogą być stosowane tylko do nazw obiektów lub funkcji. Używanie tych specyfikatorów z deklaracjami typów jest nielegalne w C ++. W C te specyfikatory są ignorowane, gdy są używane w deklaracjach typu. Przykład:
static struct S { // valid C, invalid in C++ int i; };
Uzasadnienie: Specyfikatory klas pamięci nie mają żadnego znaczenia, gdy są powiązane z typem. W C ++ członkowie klasy mogą być deklarowani za pomocą statycznego specyfikatora klasy pamięci. Dopuszczenie specyfikatorów klas pamięci w deklaracjach typu może sprawić, że kod będzie mylący dla użytkowników.
I jak struct
, class
jest także deklaracja typu.
To samo można wywnioskować, spacerując po drzewie składni w załączniku A.
Warto zauważyć, że static struct
było to legalne w C, ale nie miało żadnego skutku: dlaczego i kiedy używać struktur statycznych w programowaniu w C?
Jak zauważono tutaj, lepszym sposobem na osiągnięcie tego w C ++ może być użycie przestrzeni nazw. Ale ponieważ nikt nie wspomniał final
tutaj o tym słowie kluczowym, piszę, static class
jak mógłby wyglądać bezpośredni odpowiednik z C # w C ++ 11 lub nowszym:
class BitParser final
{
public:
BitParser() = delete;
static bool GetBitAt(int buffer, int pos);
};
bool BitParser::GetBitAt(int buffer, int pos)
{
// your code
}
Możesz „mieć” klasę statyczną w C ++, jak wspomniano wcześniej, klasa statyczna to taka, która nie ma żadnych instancji jej obiektów. W C ++ można to uzyskać, deklarując konstruktor / destruktor jako prywatny. Wynik końcowy jest taki sam.
Jest to podobne do sposobu, w jaki C # robi to w C ++
W pliku C #.cs możesz mieć prywatną zmienną wewnątrz funkcji publicznej. Znajdując się w innym pliku, możesz go użyć, wywołując przestrzeń nazw z funkcją jak w:
MyNamespace.Function(blah);
Oto jak naśladować to samo w 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
}
}
OtherFile.h
#include "SharedModule.h"
OtherFile.cpp
//Call the functions using the hidden variables
SharedData::SetError("Hello", "World");
SharedData::DisplayError();
W przeciwieństwie do innych zarządzanych języków programowania, „klasa statyczna” nie ma znaczenia w C ++. Możesz skorzystać ze statycznej funkcji członka.
Jednym z przypadków, w których przestrzenie nazw mogą nie być tak przydatne do osiągnięcia „klas statycznych”, jest użycie tych klas do uzyskania kompozycji zamiast dziedziczenia. Przestrzenie nazw nie mogą być przyjaciółmi klas, a zatem nie mogą uzyskać dostępu do prywatnych członków klasy.
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;
}
};
Jedną (z wielu) alternatywą, ale najbardziej (moim zdaniem) eleganckim (w porównaniu do używania przestrzeni nazw i prywatnych konstruktorów do emulacji statycznego zachowania) sposobem na osiągnięcie „klasy, której nie można utworzyć” w C ++ byłoby zadeklarować funkcję czysto wirtualną za pomocą private
modyfikatora dostępu.
class Foo {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
Jeśli używasz C ++ 11, możesz zrobić wszystko, aby upewnić się, że klasa nie jest dziedziczona (w celu czystej emulacji zachowania klasy statycznej), używając final
specyfikatora w deklaracji klasy, aby ograniczyć inne klasy przed dziedziczeniem .
// C++11 ONLY
class Foo final {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
Choć może to zabrzmieć głupio i nielogicznie, C ++ 11 dopuszcza deklarację „funkcji wirtualnej, której nie można zastąpić”, której można użyć obok zadeklarowania klasy final
czysto i w pełni zaimplementować zachowanie statyczne, ponieważ powoduje to powstanie wyniku klasy, aby nie była dziedziczona, a funkcja manekina nie powinna być w żaden sposób nadpisywana.
// 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