C++ で静的クラスを作成するにはどうすればよいですか? 私は次のようなことができるはずです:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
BitParser
私がクラスを作成したと仮定します。BitParser
クラス定義はどのようになりますか?
たとえば C# でできるように、"static" キーワードをクラスに適用する方法を探している場合は、Managed C++ を使用しないとできません。
しかし、サンプルの外観では、BitParser オブジェクトに public static メソッドを作成するだけです。そのようです:
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;
}
このコードを使用して、サンプル コードと同じ方法でメソッドを呼び出すことができます。
Matt Price のソリューションを考えてみましょう。
あなたが望むのは、C++ セマンティクスで表現された、関数 (関数であるため) を名前空間に配置することです。
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++ ではスタイルが悪いです。
静的プライベートメンバー変数を使用する必要がある場合があるため、静的メソッドを支持する議論がありました。
以下に示すように、私は多少同意しません。
// 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ヘッダー
// 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 の存在すら知りません!
アドレス帳を肌に刺青して裸で歩く「静的クラス」とは異なり、「匿名」名前空間は完全に服を着ており、カプセル化されているようです。
あなたのコードのユーザーが破壊工作員でない限り (演習として、ダーティ ビヘイビア未定義のハックを使用してパブリック クラスのプライベート部分にアクセスする方法を見つけてもらいます...)、たとえそれprivate
がヘッダーで宣言されたクラスprivate
のセクションに表示されます。private
それでも、プライベート メンバーにアクセスできる別の「プライベート関数」を追加する必要がある場合は、ヘッダーを変更して全世界に宣言する必要があります。これは、私に関する限りパラドックスです。私のコード (CPP 部分) の場合、インターフェイス (HPP 部分) は変更しないでください。レオニダスの言葉を引用: 「これはカプセル化です!」
クラスの静的メソッドが実際に非メンバー関数を持つ名前空間よりも優れているのはいつですか?
関数をグループ化し、そのグループをテンプレートにフィードする必要がある場合:
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 標準ドラフトAnnex 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 プログラミングで静的構造を使用するのですか?
C++ では、クラス (静的クラスではない) の静的関数を作成する必要があります。
class BitParser {
public:
...
static ... getBitAt(...) {
}
};
その後、BitParser::getBitAt() を使用して、目的の結果であると思われるオブジェクトをインスタンス化せずに関数を呼び出すことができるはずです。
前述のように、C++ で静的クラスを持つことができます。静的クラスは、インスタンス化されたオブジェクトを持たないクラスです。C++ では、コンストラクター/デストラクターをプライベートとして宣言することで取得できます。最終結果は同じです。
Managed C ++では、静的クラスの構文は次のとおりです。
public ref class BitParser abstract sealed
{
public:
static bool GetBitAt(...)
{
...
}
}
...決して遅くなるよりはまし...
これは、C++ で行う C# の方法に似ています。
C# file.cs では、パブリック関数内にプライベート var を含めることができます。別のファイルにある場合は、次のように関数で名前空間を呼び出すことで使用できます。
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
}
}
その他のファイル.h
#include "SharedModule.h"
その他のファイル.cpp
//Call the functions using the hidden variables
SharedData::SetError("Hello", "World");
SharedData::DisplayError();
(多くの) 代替案の 1 つですが、(私の意見では) 最も洗練された (静的な動作をエミュレートするために名前空間とプライベート コンストラクターを使用する場合と比較して)、C++ で「インスタンス化できないクラス」の動作を実現する方法は、private
アクセス修飾子を使用して、ダミーの純粋仮想関数を宣言します。
class Foo {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
final
C++11 を使用している場合は、クラス宣言で指定子を使用して他のクラスがそれを継承しないように制限することで、クラスが継承されないようにする (純粋に静的クラスの動作をエミュレートする) ために、さらに一歩進むことができます。.
// 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
「静的クラス」を実現するのに名前空間があまり役に立たないケースの 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;
}
};