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

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

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

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

Ответов (14)

Решение

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

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

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

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

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

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

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

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

И вроде бы struct, class это еще и объявление типа.

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

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

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

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

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

В отличие от других языков управляемого программирования, «статический класс» НЕ имеет значения в 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
class A final {
  ~A() = delete;
  static bool your_func();
}

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

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

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

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

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

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

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

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

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

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

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

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

В BitParser.h

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

В BitParser.cpp

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

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

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

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

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

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

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

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