Как создать статический класс на C++?

Как создать статический класс на C++? Я должен уметь делать что-то вроде:

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

Предполагая, что я создал класс BitParser. Как бы выглядело определение класса BitParser?

@Vagrant функция внутри пространства имен по-прежнему остается функцией. Функция, принадлежащая классу, называется методом. Если это статический метод, вы вызываете его так же, как если бы это была функция внутри пространства имен.

user1598585 27.09.2012 20:42

@ superjoe30 Насколько я знаю, есть одно хорошее применение для "статического" класса: специализация перегруженной функции-шаблона - см. "Мораль № 2" здесь.

bcrist 30.12.2013 05:01

Контейнероподобные классы IMO (имеющие только статические методы) полезны в определенных случаях.

AarCee 17.05.2018 14:16

Шаблоны статических классов можно использовать для удаления избыточных объявлений типов в нескольких шаблонах классов.

anni 20.12.2019 22:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
281
4
479 500
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

В C++ вы хотите создать статическую функцию класса (а не статический класс).

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

Затем вы сможете вызвать функцию с помощью BitParser :: getBitAt () без создания экземпляра объекта, который, как я полагаю, является желаемым результатом.

Ответ принят как подходящий

Если вы ищете способ применения ключевого слова static к классу, как, например, в 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;
}

Вы можете использовать этот код для вызова метода так же, как ваш пример кода.

Надеюсь, это поможет! Ваше здоровье.

Чтобы прояснить ваше намерение в этом подходе, вы можете дополнительно использовать частный конструктор. private: BitParser() {} Это предотвратит создание экземпляров кем-либо.

Danvil 22.07.2010 19:38

OJ, у вас есть синтаксическая ошибка. Ключевое слово static следует использовать только в определении класса, а не в определении метода.

andrewrk 13.08.2008 22:02

@MoatazElmasry потокобезопасность - проблема, когда вы делитесь состоянием. В приведенной выше реализации нет общего состояния, поэтому не может быть никаких проблем с безопасностью потоков ... если только вы не настолько глупы, чтобы использовать статику внутри для этих функций. Так что да, приведенный выше код является потокобезопасным, просто держите постоянное состояние вне ваших функций, и все хорошо.

OJ. 26.08.2012 01:25

@MoatazElmasry Неверно. Два потока не могут изменять нестатические локальные переменные в статической функции.

OJ. 27.08.2012 06:02

Если вы пытаетесь добиться аналогичного поведения Статические классы C#, вы должны пометить класс как запечатанный с помощью ключевого слова final (из C++ 11), чтобы предотвратить наследование любого другого класса.

Jämes 27.05.2017 23:57

Если бы C++ 11, я бы сказал, что лучше BitParser() = delete; правильно передать намерение удалить конструктор (а не просто скрыть его как private).

phoenix 06.07.2018 16:25

If you're looking for a way of applying the "static" keyword to a class, like you can in C# for example

статические классы - это просто компилятор, который держит вас за руку и не дает вам писать какие-либо методы / переменные экземпляра.

Если вы просто напишете обычный класс без каких-либо методов / переменных экземпляра, это то же самое, и это то, что вы бы сделали на C++.

Не жаловаться (особенно на вас), но некоторая поддержка компилятора, чтобы я не написал или не вырезал / не вставлял слово static 200 раз, было бы хорошо.

3Dave 06.11.2013 05:07

Согласен, но статический класс в C# тоже этого не делает. Он просто не компилируется, когда вы забываете вставить туда статику :-)

Orion Edwards 11.11.2013 01:29

Ага - достаточно честно. Мои макросы отображаются. Честно говоря, если я объявлю класс статическим, компилятор должен выдать ошибку, только если я попытаюсь создать его экземпляр. Правила, требующие от меня повторения, отвратительны и должны быть первыми против стены, когда наступит революция.

3Dave 11.11.2013 02:02

Вы также можете создать бесплатную функцию в пространстве имен:

В BitParser.h

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

В BitParser.cpp

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

В общем, это предпочтительный способ написания кода. Когда в объекте нет необходимости, не используйте класс.

В некоторых случаях вам может потребоваться инкапсуляция данных, даже если класс в основном «статический». Статические частные члены класса дадут вам это. Члены пространства имен всегда являются общедоступными и не могут обеспечивать инкапсуляцию данных.

Torleif 12.05.2009 23:34

Если переменная "член" объявляется только из файла .cpp, и доступ к ней осуществляется только из файла .cpp, он более частный, чем частная переменная, объявленная в файле .h. НЕ то, что я рекомендую эту технику.

jmucchiello 26.04.2010 23:03

@Torleif: Вы ошибаетесь. пространства имен лучше подходят для инкапсуляции, чем статические частные члены. См. Мой ответ для демонстрации.

paercebal 11.06.2010 02:14

да, но в пространстве имен вы должны соблюдать порядок функций, в отличие от класса со статическими членами, например void a () {b ();} b () {} приведет к ошибке в пространстве имен, но не в классе с статические члены

Moataz Elmasry 23.08.2012 17:08

Рассмотрим Решение Мэтта Прайса.

  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 все (как и во ВСЕХ) функции, которым будет разрешен к ней доступ !!!

Итак, использование частного статического члена похоже на прогулку обнаженной на улице со списком ваших любовников, вытатуированным на вашей коже: никто не имеет права прикасаться, но каждый может взглянуть на него. И бонус: каждый может иметь имена тех, кто имеет право играть с вашими личными вещами.

private действительно ... :-D

Решение "Анонимные пространства имен"

Преимущество анонимных пространств имен в том, что они делают вещи действительно приватными.

Во-первых, коллектор ГЭС

// HPP

namespace Foo
{
   void barA() ;
}

Просто чтобы быть уверенным, что вы заметили: нет бесполезного объявления barB или myGlobal. Это означает, что никто, читающий заголовок, не знает, что скрывается за barA.

Затем 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.

Это действительно важно?

Если пользователи вашего кода не являются саботажниками (я позволю вам в качестве упражнения найти, как можно получить доступ к частной части публичного класса с помощью взлома грязного поведения-undefined ...), то, что 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 !!!

Потому что, если класс может быть параметром шаблона, пространства имен не могут.

GCC поддерживает -fno-access-control, который можно использовать в модульных тестах белого ящика для доступа к другим частным членам класса. Это единственная причина, по которой я могу оправдать использование члена класса вместо анонимного / статического глобального в реализации.

Tom 10.09.2010 17:59

@Tom: Кросс-платформенным решением было бы добавить следующий код #define private public в заголовки ... ^ _ ^ ...

paercebal 10.09.2010 21:20

@Tom: в любом случае, IMHO, даже с учетом модульного тестирования, минусы "показывать слишком много вещей" перевешивают плюсы. Я предполагаю, что альтернативным решением было бы поместить тестируемый код в функцию, принимающую необходимые параметры (и не более) в пространстве имен utilities. Таким образом, эта функция может быть протестирована на единицу и по-прежнему не имеет специального доступа к закрытым членам (поскольку они задаются как параметры при вызове функции) ...

paercebal 10.09.2010 21:26

@paercebal Я собираюсь прыгнуть на борт твоего корабля, но у меня есть последнее оговорка. Если кто-то запрыгнет в ваш namespace, не получит ли он доступ к вашим global, пусть и скрытым, членам? Очевидно, им придется угадывать, но если вы намеренно не запутаете свой код, имена переменных угадать довольно легко.

Zak 20.09.2014 00:32

@Zak: Да, могли, но только пытаясь сделать это в файле CPP, где объявлена ​​переменная myGlobal. Дело в большей наглядности, чем в доступности. В статическом классе переменная myGlobal является частной, но все же видимой. Это не так важно, как кажется, но все же в DLL отображение символа, который должен быть частным для DLL в экспортированном заголовке, может быть неудобным ... В пространстве имен myGlobal существует только в файле CPP (вы можно даже пойти дальше и сделать его статичным). Эта переменная не отображается в общедоступных заголовках.

paercebal 21.09.2014 02:09

@paercebal Значит, помещая их в анонимный namespace, вы закрываете переменные? В своем ответе можете ли вы отойти от своей аналогии и объяснить механизм namespace более детально (как в наложенных ограничениях)?

Zak 23.09.2014 04:39

@paercebal, а также #define class struct для перехвата членов, которые по умолчанию являются закрытыми в начале класса. Но похоже, что это может что-то сломать? Вы видите, как это могло быть?

Tyler 24.02.2016 22:12

Вы «можете» иметь статический класс в C++, как упоминалось ранее, статический класс - это тот, который не имеет каких-либо объектов, созданных для его создания. В C++ это можно получить, объявив конструктор / деструктор закрытым. Конечный результат такой же.

То, что вы предлагаете, может создать одноэлементный класс, но это не то же самое, что статический класс.

ksinkar 17.10.2017 14:40

Это похоже на то, как это делается в 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
  }
}

OtherFile.h

#include "SharedModule.h"

OtherFile.cpp

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

Но каждый может зайти в The DataToBeHidden -> Это не решение

Guy L 11.07.2013 00:20

В Managed C++ синтаксис статического класса: -

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

... лучше поздно, чем никогда...

В отличие от других языков управляемого программирования, «статический класс» НЕ имеет значения в C++. Вы можете использовать статическую функцию-член.

Можно написать что-нибудь вроде static class?

Нет, согласно Приложению C 7.1.1 Стандартный проект C++ 11 N3337:

Change: In C++, the static or extern specifiers can only be applied to names of objects or functions. Using these specifiers with type declarations is illegal in C++. In C, these specifiers are ignored when used on type declarations. Example:

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

Rationale: Storage class specifiers don’t have any meaning when associated with a type. In C++, class members can be declared with the static storage class specifier. Allowing storage class specifiers on type declarations could render the code confusing for users.

Как и struct, class также является объявлением типа.

То же самое можно сделать, просмотрев дерево синтаксиса в Приложении A.

Интересно отметить, что 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
}

Один случай, когда пространства имен могут быть не так полезны для достижения «статических классов», - это использование этих классов для достижения композиции вместо наследования. Пространства имен не могут быть друзьями классов и поэтому не могут получить доступ к закрытым членам класса.

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

class A final {
  ~A() = delete;
  static bool your_func();
}

final означает, что класс не может быть унаследован от.

delete для деструктора означает, что вы не можете создать экземпляр такого класса.

Этот шаблон также известен как класс "util".

Как многие говорят, в C++ не существует концепции static class.

Канонический namespace, содержащий функции static, предпочтителен в качестве решения в этом случае.

Другие вопросы по теме