Как вы создаете статический класс в C ++? - PullRequest
234 голосов
/ 13 августа 2008

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

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

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

Ответы [ 12 ]

248 голосов
/ 13 августа 2008

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

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

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

232 голосов
/ 22 сентября 2008

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

  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.

Но если вы внимательно посмотрите на это, вы обнаружите, что это колоссальная ошибка: не только ваша частная переменная все равно должна быть объявлена ​​в ГЭС (и, таким образом, видимой для всего мира, несмотря на то, что является частной), но Вы должны объявить в одной и той же 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.

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

Если только пользователи вашего кода не являются диверсантами (в качестве упражнения я покажу вам, как можно получить доступ к закрытой части открытого класса, используя хак с грязным поведением, не определенным ...), что такое private равно private, даже если оно отображается в разделе private класса, объявленного в заголовке.

Тем не менее, если вам нужно добавить еще одну "приватную функцию" с доступом к приватному члену, вы все равно должны объявить ее всему миру, изменив заголовок, что, на мой взгляд, парадокс: Если я изменю реализацию своего кода (часть CPP), то интерфейс (часть HPP) НЕ должен измениться. Цитата Леонидаса: " Это ENCAPSULATION! "

Редактировать 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 !!!

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

60 голосов
/ 13 августа 2008

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

В BitParser.h

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

In BitParser.cpp

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

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

12 голосов
/ 13 августа 2008

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

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

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

11 голосов
/ 13 августа 2008

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

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

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

10 голосов

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

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

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

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

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

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

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

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

5 голосов
/ 24 ноября 2009

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

4 голосов
/ 10 июня 2010

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

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

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

3 голосов
/ 29 января 2014

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

3 голосов
/ 26 апреля 2010

Это похоже на способ 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();
...