Як створити статичний клас у C ++? Я повинен вміти робити щось на кшталт:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Якщо припустити, що я створив BitParser
клас. Як BitParser
виглядатиме визначення класу?
Як створити статичний клас у C ++? Я повинен вміти робити щось на кшталт:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Якщо припустити, що я створив BitParser
клас. Як BitParser
виглядатиме визначення класу?
Відповіді:
Якщо ви шукаєте спосіб застосування "статичного" ключового слова до класу, як, наприклад, у C #, тоді ви не зможете без використання керованого 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;
}
Ви можете використовувати цей код для виклику методу так само, як і ваш код прикладу.
Сподіваюся, що це допомагає! Ура.
private: BitParser() {}
Це не дозволить комусь створити екземпляри.
BitParser() = delete;
правильно передати намір видалити конструктор (а не просто приховати його як private
).
Розгляньте рішення Метта Прайса .
Що ви хочете, виражене в семантиці C ++, щоб поставити функцію (вона є функцією) в просторі імен.
У 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 ++ це поганий стиль.
Був аргумент на користь статичного методу, оскільки іноді потрібно використовувати статичну змінну приватного члена.
Я дещо не згоден, як показано нижче:
// 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 здається хорошою з точки зору інкапсуляції ... Це здорово, тому що хтось, хто дивиться на ГЕС, не зможе (якщо не вдасться до саботажу) отримати доступ Foo :: myGlobal.
Але якщо ви уважно придивитесь до цього, то виявите, що це колосальна помилка: не тільки ваша приватна змінна все ще повинна бути оголошена в ГЕС (і так, видима для всього світу, незважаючи на приватність), але ви повинні заявити в тій же ГЕС всі (як і ВСІ) функції, які матимуть право доступу до неї !!!
Тож використання приватного статичного члена - це як виходити на вулицю з оголеним списком ваших закоханих, татуйованих на вашій шкірі: ніхто не має права торкатися, але кожен може зазирнути. І бонус: кожен може мати імена тих, хто має право грати з вашими призами.
private
справді ... :-D
Анонімні простори імен матимуть перевагу в тому, щоб зробити речі справді приватними.
По-перше, заголовок ГЕС
// HPP
namespace Foo
{
void barA() ;
}
Просто для переконання, що ви зауважили: Не існує марної декларації barB чи myGlobal. Що означає, що ніхто не читає заголовка не знає, що ховається за барA.
Потім 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 !!!
}
Як бачимо, як і так зване декларування "статичного класу", fooA і fooB все ще можуть отримати доступ до myGlobal. Але ніхто більше не може. І ніхто більше поза цією CPP не знає, що fooB і myGlobal навіть існують!
На відміну від "статичного класу", що ходить по ню з татуйованою на її шкірі адресною книжкою, "анонімний" простір імен повністю одягнений , що здається набагато краще зафіксованим AFAIK.
Якщо користувачі вашого коду не є диверсантами (я дозволю вам, як вправу дізнатись, як можна отримати доступ до приватної частини публічного класу, використовуючи брудний не визначений хакер ...), що private
таке private
, навіть якщо це видно в private
розділі класу, оголошеного в заголовку.
Тим не менш, якщо вам потрібно додати ще одну "приватну функцію" з доступом до приватного члена, ви все одно повинні оголосити її всьому світу, змінивши заголовок, що є парадоксам, що стосується мене: Якщо я змінити реалізацію мій код (CPP частина), то інтерфейс (частина HPP) НЕ повинен змінюватися. Цитуючи Леонідаса: " Це ЕНКАПСУЛАЦІЯ! "
Коли статичні методи класів насправді кращі, ніж простори імен з не членами функцій?
Коли потрібно згрупувати функції та подати групу до шаблону:
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 !!!
Тому що, якщо клас може бути параметром шаблону, простори імен не можуть.
#define private public
у заголовки ... ^ _ ^ ...
utilities
просторі імен. Таким чином, ця функція може бути перевірена одиницями, і все ще не мати спеціального доступу до приватних членів (оскільки вони задаються як параметри при виклику функції) ...
namespace
волі, він не отримає доступу до ваших global
, хоч і прихованих, членів? Очевидно, їм доведеться здогадуватися, але якщо ви навмисно не заблукаєте кодом, імена змінних досить легко здогадатися.
Ви також можете створити безкоштовну функцію в просторі імен:
У 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 ++
static
було б добре.
У C ++ ви хочете створити статичну функцію класу (а не статичний клас).
class BitParser {
public:
...
static ... getBitAt(...) {
}
};
Тоді ви зможете викликати функцію за допомогою BitParser :: getBitAt (), не ініціюючи об'єкт, який я вважаю, є бажаним результатом.
Чи можу я написати щось на кшталт static class
?
Ні , згідно з проектом додатка C 7.1.1 стандарту C ++ 11 N3337 :
Зміна: у C ++ статичні або зовнішні специфікатори можуть застосовуватися лише до імен об'єктів або функцій. Використання цих специфікаторів з деклараціями типу незаконно в C ++. У C ці специфікатори ігноруються при використанні в деклараціях типу. Приклад:
static struct S { // valid C, invalid in C++ int i; };
Обгрунтування: специфікатори класу зберігання не мають жодного значення, коли асоціюються з типом. У C ++ члени класу можуть бути оголошені статичним специфікатором класу зберігання. Дозволення специфікаторів класу зберігання для декларацій типу може зробити код заплутаним для користувачів.
І, як struct
, class
також є декларація про тип.
Те саме можна визначити, пройшовши синтаксичне дерево у Додатку А.
Цікаво відзначити, що це static struct
було законно в C, але не мало ефекту: навіщо і коли використовувати статичні структури в програмуванні на 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
}
Ви можете "мати" статичний клас у C ++, як згадувалося раніше, статичний клас - це той, у якого немає жодних об'єктів, які його інстанціювали. У C ++ це можна отримати, оголосивши конструктор / деструктор приватним. Кінцевий результат той самий.
Це схоже на спосіб C # робити це в C ++
У C # file.cs ви можете мати приватний var всередині публічної функції. Якщо в іншому файлі, ви можете використовувати його, викликаючи простір імен з функцією, як у:
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
}
}
OtherFile.h
#include "SharedModule.h"
OtherFile.cpp
//Call the functions using the hidden variables
SharedData::SetError("Hello", "World");
SharedData::DisplayError();
На відміну від інших керованих мов програмування, "статичний клас" не має значення на C ++. Ви можете використовувати статичну функцію члена.
Один з випадків, коли простори імен можуть бути не настільки корисними для досягнення "статичних класів", - це при використанні цих класів для досягнення складу над успадкуванням. Простори імен не можуть бути друзями класів і тому не можуть отримати доступ до приватних членів класу.
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;
}
};
Один (із безлічі) альтернативних, але самий (на мій погляд) елегантний (порівняно з використанням просторів імен та приватних конструкторів для імітації статичної поведінки), спосіб досягти поведінки "класу, який неможливо інстанціювати" у C ++ оголосити фіктивну чисту віртуальну функцію за допомогою private
модифікатора доступу.
class Foo {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
Якщо ви використовуєте C ++ 11, ви можете пройти додаткову милю, щоб переконатися, що клас не успадковується (щоб чисто імітувати поведінку статичного класу), використовуючи final
специфікатор у декларації класу, щоб обмежити інші класи від успадкування його .
// C++11 ONLY
class Foo final {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
Як би нерозумно і нелогічно це не звучало, C ++ 11 дозволяє оголосити "чисту віртуальну функцію, яку неможливо змінити", яку ви можете використовувати разом з декларуванням класу final
для чистої та повної реалізації статичної поведінки, оскільки це призводить до результату клас, який не має бути успадкованим, а фіктивна функція ні в якому разі не перекривається.
// 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