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

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

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

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

12.08.2008 23:35:00
Ага. В старые времена мы просто называли это «функцией». Вы, дети, сегодня со своими сумасшедшими "пространствами имен". Эй, сойди с моей лужайки! <Встряхивает кулак>
Vagrant 10.06.2010 15:41:50
@ Vagrant функция внутри пространства имен по-прежнему является функцией. Функция, которая принадлежит классу, называется методом. Если это статический метод, вы вызываете его аналогично, как если бы это была функция внутри пространства имен.
Mario 27.09.2012 16:42:06
Спустя 5 лет и сейчас я перешёл на сторону @Vagrant. Что за глупый вопрос!
andrewrk 6.12.2013 21:05:59
Спустя 9 лет, и теперь у меня есть свой собственный язык программирования, который даже не имеет классов: ziglang.org
andrewrk 30.09.2017 22:14:33
Контейнерные классы IMO (имеющие только статические методы) полезны в некоторых случаях.
AarCee 17.05.2018 11:16:53
12 ОТВЕТОВ
РЕШЕНИЕ

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

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

Надеюсь, это поможет! Приветствия.

270
25.07.2016 17:32:02
OJ, у вас есть синтаксическая ошибка . Ключевое слово static должно использоваться только в определении класса, а не в определении метода.
andrewrk 13.08.2008 18:02:01
Чтобы прояснить свое намерение в этом подходе, вы можете дополнительно использовать приватный конструктор. private: BitParser() {}Это не позволит никому создавать экземпляры.
Danvil 22.07.2010 15:38:57
Безопасность потока @MoatazElmasry - проблема, когда вы делитесь состоянием. В приведенной выше реализации нет общего состояния, поэтому не может быть проблем с безопасностью потоков ... если вы не настолько глупы, чтобы использовать статические функции внутри этих функций. Так что да, приведенный выше код является потокобезопасным, просто держите постоянное состояние в своих функциях, и все в порядке.
OJ. 25.08.2012 21:25:35
@MoatazElmasry Неверно. Два потока не могут изменять нестатические локальные переменные в статической функции.
OJ. 27.08.2012 02:02:59
Если бы C ++ 11, я бы сказал, что лучше BitParser() = delete;правильно передать намерение удалить конструктор (а не просто скрыть его как private).
phoenix 6.07.2018 13:25:17

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

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

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

11
12.08.2008 23:43:34

Если вы ищете способ применения «статического» ключевого слова к классу, как вы можете, например, в C #

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

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

13
13.08.2008 00:06:09
Не жаловаться (особенно на вас), но staticбыло бы неплохо, если бы кто-то держал меня за компилятор, чтобы я не писал или не вырезал / не вставил слово 200 раз.
3Dave 6.11.2013 01:07:43
Согласен - но статический класс в C # тоже этого не делает. Он просто не скомпилируется, когда вы забудете вставить туда статические данные :-)
Orion Edwards 10.11.2013 21:29:10
Да, достаточно честно. Мои макросы показывают. Честно говоря, если я объявляю класс статическим, компилятор должен выдавать ошибку, только если я пытаюсь его создать. Правила, которые требуют от меня повторения, неприятны и должны быть первыми, когда наступит революция.
3Dave 10.11.2013 22:02:26

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

В BitParser.h

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

В BitParser.cpp

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

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

63
13.08.2008 00:26:43
В некоторых случаях вам может потребоваться инкапсуляция данных, даже если класс в основном «статический». Статические члены частного класса дадут вам это. Члены пространства имен всегда общедоступны и не могут обеспечить инкапсуляцию данных.
Torleif 12.05.2009 19:34:35
Если переменная "member" объявлена ​​и доступна только из файла .cpp, она является более закрытой, чем частная переменная, объявленная в файле .h. НЕ то, что я рекомендую эту технику.
jmucchiello 26.04.2010 19:03:07
@ Торлейф: Ты не прав. пространства имен лучше подходят для инкапсуляции, чем статические закрытые члены. Смотрите мой ответ для демонстрации.
paercebal 10.06.2010 22:14:48
да, но в пространстве имен вы должны поддерживать порядок функций, в отличие от класса со статическими членами, например, void a () {b ();} b () {} приведет к ошибке в пространстве имен, но не в классе с статические участники
Moataz Elmasry 23.08.2012 13:08:02

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

  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

Был аргумент в пользу статического метода, потому что иногда нужно использовать статическую закрытую переменную-член.

Я не согласен, как показано ниже:

Решение "Static private member"

// 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 действительно ...

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

Преимущество анонимных пространств имен в том, что они становятся частными.

Во-первых, заголовок ГЭС

// 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.

Это действительно имеет значение?

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

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

244
5.07.2018 12:48:43
GCC поддерживает -fno-access-control, который можно использовать в модульных тестах whitebox для доступа к закрытым членам класса. Это единственная причина, по которой я могу оправдать использование члена класса вместо анонимного / статического глобального в реализации.
Tom 10.09.2010 13:59:50
@Tom: кроссплатформенным решением было бы добавить следующий код #define private publicв заголовки ... ^ _ ^ ...
paercebal 10.09.2010 17:20:33
@Tom: во всяком случае, ИМХО, даже учитывая модульное тестирование, минусы «показа слишком большого количества материала» перевешивают плюсы. Я предполагаю, что альтернативным решением было бы поместить тестируемый код в функцию, принимающую необходимые параметры (и не более) в utilitiesпространстве имен. Таким образом, эта функция может быть проверена модулем и все еще не имеет специального доступа к закрытым членам (так как они задаются в качестве параметров при вызове функции) ...
paercebal 10.09.2010 17:26:33
@paercebal Я собираюсь запрыгнуть на ваш корабль, но у меня есть одно последнее замечание. Если кто-то прыгнет в вашу namespaceволю, он не получит доступ к вашим global, хотя и скрытым, участникам? Очевидно, им придется угадывать, но если вы не намеренно запутываете свой код, имена переменных довольно легко угадать.
Zak 19.09.2014 20:32:13
@Zak: Действительно, они могли, но только пытаясь сделать это в файле CPP, где объявлена ​​переменная myGlobal. Дело в большей видимости, чем в доступности. В статическом классе переменная myGlobal является закрытой, но все еще видимой. Это не так важно, как кажется, но, тем не менее, в DLL, показ символа, который должен быть закрытым для DLL в экспортированном заголовке, может быть неудобным ... В пространстве имен myGlobal существует только в файле CPP (вы может даже пойти дальше и сделать его статичным). Эта переменная не появляется в общедоступных заголовках.
paercebal 20.09.2014 22:09:02

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

5
23.11.2009 21:04:27
То, что вы предлагаете, может создать одноэлементный класс, но это не то же самое, что статический класс.
ksinkar 17.10.2017 11:40:57

Это похоже на способ 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();
3
27.04.2010 02:38:36
Но каждый может перейти к TheDataToBeHidden -> Это не решение проблемы
Guy L 10.07.2013 20:20:37

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

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

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

4
10.06.2010 15:35:29

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

3
29.01.2014 06:34:41

Могу я написать что-нибудь подобное static class?

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

Изменение: в C ++ статические или внешние указатели могут применяться только к именам объектов или функций. Использование этих спецификаторов с объявлениями типов недопустимо в C ++. В Си эти спецификаторы игнорируются при использовании в объявлениях типов. Пример:

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

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

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

То же самое можно сделать, пройдя по синтаксическому дереву в Приложении А.

Интересно отметить, что это static structбыло допустимо в C, но не дало эффекта: зачем и когда использовать статические структуры в C-программировании?

11
17.11.2017 08:37:50

Как уже отмечалось, лучшим способом достижения этого в 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
}
5
21.03.2018 11:52:10

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

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
25.04.2018 07:47:11