C ++で静的クラスをどのように作成しますか?


263

C ++で静的クラスをどのように作成しますか?私は次のようなことができるはずです:

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

BitParserクラスを作成したと仮定します。何だろうBitParserようなクラス定義を見て?


198
うん。昔は、それを「関数」と呼んでいました。あなたはあなたのクレイジーな「名前空間」で今日子供です。ねえ、私の芝生を降りて!<拳を振る>
Vagrant

7
@Vagrant名前空間内の関数は引き続き関数です。クラスに属する関数はメソッドと呼ばれます。静的メソッドの場合は、名前空間内の関数の場合と同様に呼び出します。

48
5年後、私は@Vagrantを使用しています。なんてばかげた質問だ!
andrewrk 2013

16
9年後、今私も授業を持っていないことを私自身のプログラミング言語を持っている:ziglang.org
andrewrk

1
IMOコンテナーのようなクラス(静的メソッドのみを持つ)は、特定の場合に役立ちます。
AarCee 2018年

回答:


272

たとえばC#でできるように、クラスに「静的」キーワードを適用する方法を探している場合は、マネージ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;
}

このコードを使用して、サンプルコードと同じ方法でメソッドを呼び出すことができます。

お役に立てば幸いです。乾杯。


2
OJ、構文エラーあります。staticキーワードは、クラス定義でのみ使用し、メソッド定義では使用しないでください。
アンドリューク2008

90
このアプローチで意図を明確にするために、さらにプライベートコンストラクターを使用できます。private: BitParser() {}これにより、誰もインスタンスを作成できなくなります。
Danvil

7
@MoatazElmasryスレッドの安全性は、状態を共有するときに問題になります。上記の実装では状態が共有されないため、これらの関数内で静的関数を使用できるほど愚かでない限り、スレッドの安全性に問題はありません。ですから、上記のコードはスレッドセーフであり、永続的な状態を関数の中に入れないでください。
OJ。

@MoatazElmasry不正解です。2つのスレッドは、静的関数の非静的ローカル変数を変更できません。
OJ。

12
C ++ 11の場合BitParser() = delete;、コンストラクターを削除する意図を(それをとして非表示にするだけでなくprivate)適切に伝える方がよいと主張します。
フェニックス

247

Matt Priceのソリューションを考えてみましょう。

  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静的メソッド」パターンのクラスよりも強力です。

  • 静的メソッドはクラスのプライベートシンボルにアクセスできます
  • プライベート静的メソッドは依然として(アクセスできない場合は)すべてのユーザーに表示され、カプセル化に多少違反します
  • 静的メソッドは前方宣言できません
  • ライブラリヘッダーを変更せずに、静的メソッドをクラスユーザーがオーバーロードすることはできません
  • 同じ名前空間内の(おそらく友達の)非メンバー関数よりも上手くできない静的メソッドでできることは何もありません
  • 名前空間には独自のセマンティクスがあります(組み合わせることも、匿名にすることもできます)。

結論:C ++でそのJava / 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内で(ALLと同様に)すべての機能にアクセスすることが許可されます!!!

つまり、プライベートスタティックメンバーを使用することは、恋人のリストが皮膚に刺青された状態でヌードの外を歩くようなものです。誰も触れることは許可されていませんが、誰でも覗くことができます。そしてボーナス:誰もがあなたのプリビと遊ぶことを許可された人の名前を持つことができます。

private 確かに... :-D

「匿名の名前空間」ソリューション

匿名の名前空間には、物事を本当にプライベートにするという利点があります。

まず、HPPヘッダー

// 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をかなりカプセル化したようです。

それは本当に重要ですか?

あなたのコードのユーザーが工作員でない限り(私は...あなたは、練習として、1が汚い行動未定義ハックを使用して、パブリッククラスのプライベートの部分にアクセスする方法を見つけるもらおう)、何して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 !!!

というのも、クラスをテンプレートパラメータにできる場合、名前空間はできません。


3
GCCは-fno-access-controlをサポートしています。これは、ホワイトボックスユニットテストで使用して、それ以外の場合はプライベートクラスメンバーにアクセスできます。これが、実装で匿名/静的グローバルの代わりにクラスメンバーを使用することを正当化できると考える唯一の理由です。
トム

8
@Tom:クロスプラットフォームのソリューションは#define private public、ヘッダーに次のコードを追加することです... ^ _ ^ ...
paercebal

1
@Tom:とにかく、私見では、ユニットテストを考慮しても、「あまりにも多くのものを表示する」というデメリットがプロよりも優れています。別の解決策は、utilities名前空間で必要なパラメーター(それ以上ではない)を取る関数にテストされるコードを配置することだと思います。このようにして、この関数は単体テストでき、プライベートメンバーへの特別なアクセス権はありません(関数呼び出しでパラメーターとして指定されるため)...
paercebal

@paercebal船に飛び乗るところですが、最後の予約があります。誰かがあなたのnamespace意志に飛び込んだ場合、彼らはあなたのglobalにアクセスできません。当然、推測する必要がありますが、コードを意図的に難読化しない限り、変数名はかなり簡単に推測できます。
Zak

@Zak:確かに可能ですが、myGlobal変数が宣言されているCPPファイルでそれを行おうとするだけです。ポイントは、アクセシビリティよりも視認性です。静的クラスでは、myGlobal変数はプライベートですが、まだ表示されています。これは見た目ほど重要ではありませんが、それでもDLLでは、エクスポートされたヘッダーでDLLにプライベートである必要があるシンボルを表示するのは厄介です...名前空間では、myGlobalはCPPファイルにのみ存在します(さらに遠くに移動して静的にすることもできます)。その変数はパブリックヘッダーに表示されません。
paercebal 2014

63

名前空間に無料の関数を作成することもできます。

BitParser.h内

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

BitParser.cpp内

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

一般に、これはコードを記述するための好ましい方法です。オブジェクトが必要ない場合は、クラスを使用しないでください。


1
クラスがほとんど「静的」であっても、データをカプセル化したい場合があります。静的プライベートクラスメンバーがこれを提供します。名前空間のメンバーは常にパブリックであり、データのカプセル化を提供できません。
Torleif 2009年

「メンバー」変数が宣言され、.cppファイルからのみアクセスされる場合、.hファイルで宣言されたプライベート変数よりもプライベートになります。私はこの手法をお勧めしません。
jmucchiello 2010

3
@Torleif:あなたは間違っている。名前空間は、静的プライベートメンバーよりもカプセル化に適しています。デモについては私の回答をご覧ください。
paercebal

1
はい。ただし、名前空間では関数の順序を維持する必要があります。たとえば、静的メンバーを持つクラスとは異なり、たとえばvoid a(){b();} b(){}は名前空間でエラーを生成しますが、静的メンバー
Moataz Elmasry 2012

13

たとえば、C#でできるように、クラスに「静的」キーワードを適用する方法を探している場合

静的クラスはコンパイラーであり、インスタンスのメソッドや変数の記述を阻止します。

インスタンスメソッド/変数を使用せずに通常のクラスを作成する場合も同じです。これはC ++で行うことです。


文句を言うのではなく(特にあなたに)、私が単語をstatic200回書き込んだり、切り取ったり貼り付けたりできないようにコンパイラを手で押さえておくとよいでしょう。
3Dave 2013年

同意-しかし、C#の静的クラスはこれも行いません。そこにstaticを貼り付けるのを忘れると、コンパイルに失敗します:-)
Orion Edwards

ええ-十分に公正です。マクロが表示されています。正直なところ、クラスを静的として宣言した場合、インスタンス化しようとするとコンパイラーがエラーをスローするだけです。自分自身を繰り返すことを要求するルールは厄介であり、革命が来たときに壁に最初に当たるはずです。
3Dave

11

C ++では、(静的クラスではなく)クラスの静的関数を作成します。

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

その後、BitParser :: getBitAt()を使用して、目的の結果であると私が推測するオブジェクトをインスタンス化せずに関数を呼び出すことができるはずです。


11

次のようなものを書けstatic classますか?

いいえC ++ 11 N3337標準草案 Annex C 7.1.1 によると、

変更:C ++では、staticまたはextern指定子は、オブジェクトまたは関数の名前にのみ適用できます。型宣言でこれらの指定子を使用することは、C ++では不正です。Cでは、型宣言でこれらの指定子を使用すると無視されます。例:

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

理論的根拠:ストレージクラス指定子は、型に関連付けられている場合は何の意味もありません。C ++では、静的ストレージクラス指定子を使用してクラスメンバーを宣言できます。型宣言でストレージクラス指定子を許可すると、コードがユーザーを混乱させる可能性があります。

と同様にstructclass型宣言でもあります。

同じことは、附属書Aの構文ツリーをたどることによって推定できます。

これはstatic structCでは合法でしたが効果はありませんでした。興味深いのは、Cプログラミングで静的構造体を使用する理由と時期を教えてください。


6

ここで述べたように、C ++でこれを実現するより良い方法は、名前空間を使用することです。しかし、finalここでは誰もキーワードを言及していないためstatic class、C ++ 11以降でのC#の直接の同等物がどのようになるかを投稿します。

class BitParser final
{
public:
  BitParser() = delete;

  static bool GetBitAt(int buffer, int pos);
};

bool BitParser::GetBitAt(int buffer, int pos)
{
  // your code
}

5

前に述べたように、C ++で静的クラスを「持つ」ことができます。静的クラスは、インスタンス化されたオブジェクトを持たないクラスです。C ++では、コンストラクタ/デストラクタをプライベートとして宣言することで取得できます。最終結果は同じです。


あなたが提案しているものはシングルトンクラスを作成するかもしれませんが、それは静的クラスと同じではありません。
ksinkar 2017年

4

マネージC ++では、静的クラスの構文は次のとおりです。

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

...遅くならないより...


3

これは、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();

2
しかし、誰でもTheDataToBeHiddenにアクセスできます->これは解決策ではありません
Guy L

3

他のマネージプログラミング言語とは異なり、「静的クラス」はC ++では意味がありません。静的メンバー関数を利用できます。


0

名前空間が「静的クラス」を実現するのにあまり役立たない可能性がある1つのケースは、これらのクラスを使用して継承より構成を実現する場合です。名前空間はクラスのフレンドになることはできません。したがって、クラスのプライベートメンバーにアクセスすることはできません。

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

(多くの)選択肢の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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.