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

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

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

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

15 ответов

Решение

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

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

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

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

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

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

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

В 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 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-программировании?

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

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

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

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

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

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

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

Это похоже на способ 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++ - это объявить фиктивную чистую виртуальную функцию с 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 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;
  }
};
Другие вопросы по тегам