Bagaimana Anda membuat kelas statis di C ++? Saya harus dapat melakukan sesuatu seperti:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Dengan asumsi saya membuat BitParser
kelas. Seperti apa BitParser
definisi kelas itu?
Bagaimana Anda membuat kelas statis di C ++? Saya harus dapat melakukan sesuatu seperti:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
Dengan asumsi saya membuat BitParser
kelas. Seperti apa BitParser
definisi kelas itu?
Jawaban:
Jika Anda mencari cara untuk menerapkan kata kunci "statis" ke suatu kelas, seperti yang Anda dapat di C # misalnya, maka Anda tidak akan dapat melakukannya tanpa menggunakan C ++ yang Dikelola.
Tapi sepertinya sampel Anda, Anda hanya perlu membuat metode statis publik pada objek BitParser Anda. Seperti itu:
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;
}
Anda dapat menggunakan kode ini untuk memanggil metode dengan cara yang sama seperti kode contoh Anda.
Semoga itu bisa membantu! Bersulang.
private: BitParser() {}
Ini akan mencegah siapa pun membuat instance.
BitParser() = delete;
baik menyampaikan maksud untuk menghapus konstruktor dengan benar (tidak hanya menyembunyikannya private
).
Pertimbangkan solusi Matt Price .
Apa yang Anda inginkan adalah, dinyatakan dalam semantik C ++, untuk menempatkan fungsi Anda (karena itu adalah fungsi) dalam namespace.
Tidak ada "kelas statis" di C ++. Konsep terdekat adalah kelas dengan hanya metode statis. Sebagai contoh:
// header
class MyClass
{
public :
static void myMethod() ;
} ;
// source
void MyClass::myMethod()
{
// etc.
}
Tetapi Anda harus ingat bahwa "kelas statis" adalah retasan dalam jenis bahasa seperti Java (misalnya C #) yang tidak dapat memiliki fungsi non-anggota, jadi mereka harus memindahkannya di dalam kelas sebagai metode statis.
Di C ++, yang Anda inginkan adalah fungsi non-anggota yang akan Anda deklarasikan di namespace:
// header
namespace MyNamespace
{
void myMethod() ;
}
// source
namespace MyNamespace
{
void myMethod()
{
// etc.
}
}
Di C ++, namespace lebih kuat daripada kelas untuk pola "Java static method", karena:
Kesimpulan: Jangan menyalin / menempelkan pola Java / C # di C ++. Dalam Java / C #, polanya wajib. Tetapi dalam C ++, itu adalah gaya yang buruk.
Ada argumen yang mendukung metode statis karena kadang-kadang, kita perlu menggunakan variabel anggota pribadi statis.
Saya agak tidak setuju, seperti yang ditunjukkan di bawah ini:
// HPP
class Foo
{
public :
void barA() ;
private :
void barB() ;
static std::string myGlobal ;
} ;
Pertama, myGlobal disebut myGlobal karena masih merupakan variabel pribadi global. Melihat sumber CPP akan menjelaskan bahwa:
// 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 !!!
}
Pada pandangan pertama, fakta bahwa fungsi barC gratis tidak dapat mengakses Foo :: myGlobal tampaknya merupakan hal yang baik dari sudut pandang enkapsulasi ... Ini keren karena seseorang yang melihat HPP tidak akan dapat (kecuali menggunakan sabotase) untuk mengakses Foo :: myGlobal.
Tetapi jika Anda melihatnya dengan seksama, Anda akan menemukan bahwa itu adalah kesalahan kolosal: Tidak hanya variabel pribadi Anda masih harus dinyatakan dalam HPP (dan karenanya, terlihat oleh seluruh dunia, meskipun bersifat pribadi), tetapi Anda harus mendeklarasikan dalam HPP yang sama semua (seperti dalam SEMUA) fungsi yang akan diizinkan untuk mengaksesnya !!!
Jadi menggunakan anggota statis pribadi seperti berjalan di luar telanjang dengan daftar kekasih Anda bertato di kulit Anda: Tidak ada yang diizinkan untuk menyentuh, tetapi semua orang dapat mengintip. Dan bonusnya: Setiap orang dapat memiliki nama-nama yang berwenang bermain dengan hadiah Anda.
private
memang ... :-D
Ruang nama anonim akan memiliki keuntungan membuat hal-hal pribadi menjadi sangat pribadi.
Pertama, tajuk HPP
// HPP
namespace Foo
{
void barA() ;
}
Hanya untuk memastikan Anda berkomentar: Tidak ada deklarasi barB atau myGlobal yang tidak berguna. Yang berarti bahwa tidak ada yang membaca tajuk yang tahu apa yang tersembunyi di balik barA.
Kemudian, 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 !!!
}
Seperti yang Anda lihat, seperti deklarasi "kelas statis", fooA dan fooB masih dapat mengakses myGlobal. Tapi tidak ada orang lain yang bisa. Dan tidak ada orang lain di luar CPP ini yang tahu fooB dan myGlobal bahkan ada!
Tidak seperti "kelas statis" yang berjalan telanjang dengan buku alamatnya bertato di kulitnya, namespace "anonim" sepenuhnya berpakaian , yang tampaknya AFAIK dienkapsulasi lebih baik.
Kecuali pengguna kode Anda penyabot (Aku akan membiarkan Anda, sebagai latihan, menemukan bagaimana seseorang dapat mengakses ke bagian pribadi dari public class menggunakan kotor hack perilaku-terdefinisi ...), apa private
yang private
, bahkan jika itu terlihat di private
bagian kelas yang dinyatakan dalam header.
Namun, jika Anda perlu menambahkan "fungsi pribadi" lain dengan akses ke anggota pribadi, Anda masih harus mendeklarasikannya ke seluruh dunia dengan memodifikasi header, yang merupakan paradoks sejauh yang saya ketahui: Jika saya mengubah implementasi kode saya (bagian CPP), maka antarmuka (bagian HPP) TIDAK boleh berubah. Mengutip Leonidas: " Ini ENCAPSULASI! "
Kapan kelas metode statis sebenarnya lebih baik daripada ruang nama dengan fungsi non-anggota?
Saat Anda perlu mengelompokkan fungsi-fungsi dan memberi makan grup itu ke templat:
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 !!!
Karena, jika kelas bisa menjadi parameter templat, ruang nama tidak bisa.
#define private public
di header ... ^ _ ^ ...
utilities
namespace. Dengan cara ini, fungsi ini dapat diuji unit, dan masih tidak memiliki akses khusus ke anggota pribadi (seperti yang diberikan sebagai parameter pada panggilan fungsi) ...
namespace
mereka tidak akan mendapatkan akses ke anggota Anda global
, meskipun disembunyikan,? Jelas mereka harus menebak, tetapi kecuali Anda sengaja mengaburkan kode Anda, nama variabel cukup mudah ditebak.
Anda juga dapat membuat fungsi gratis di namespace:
Di BitParser.h
namespace BitParser
{
bool getBitAt(int buffer, int bitIndex);
}
Di BitParser.cpp
namespace BitParser
{
bool getBitAt(int buffer, int bitIndex)
{
//get the bit :)
}
}
Secara umum ini akan menjadi cara yang disukai untuk menulis kode. Ketika tidak perlu untuk objek tidak menggunakan kelas.
Jika Anda mencari cara untuk menerapkan kata kunci "statis" ke suatu kelas, seperti Anda dapat menggunakan C # misalnya
kelas statis hanyalah kompiler yang memegang Anda dan menghentikan Anda dari menulis metode / variabel instan.
Jika Anda hanya menulis kelas normal tanpa metode instance / variabel, itu adalah hal yang sama, dan ini adalah apa yang akan Anda lakukan di C ++
static
200 kali akan menjadi hal yang baik.
Di C ++ Anda ingin membuat fungsi statis suatu kelas (bukan kelas statis).
class BitParser {
public:
...
static ... getBitAt(...) {
}
};
Anda kemudian dapat memanggil fungsi menggunakan BitParser :: getBitAt () tanpa membuat instance objek yang saya anggap sebagai hasil yang diinginkan.
Bisakah saya menulis sesuatu seperti static class
?
Tidak , menurut draft standar C ++ 11 N3337, Annex C 7.1.1:
Ubah: Dalam C ++, penentu statis atau eksternal hanya dapat diterapkan pada nama objek atau fungsi. Menggunakan specifier ini dengan deklarasi tipe adalah ilegal di C ++. Dalam C, penentu ini diabaikan ketika digunakan pada deklarasi tipe. Contoh:
static struct S { // valid C, invalid in C++ int i; };
Dasar Pemikiran: Penentu kelas penyimpanan tidak memiliki arti apa pun saat dikaitkan dengan suatu jenis. Dalam C ++, anggota kelas dapat dideklarasikan dengan specifier kelas penyimpanan statis. Mengizinkan specifier kelas penyimpanan pada deklarasi tipe dapat membuat kode membingungkan bagi pengguna.
Dan suka struct
, class
juga merupakan jenis deklarasi.
Hal yang sama dapat disimpulkan dengan berjalan di pohon sintaks di Lampiran A.
Sangat menarik untuk dicatat bahwa static struct
itu legal di C, tetapi tidak berpengaruh: Mengapa dan kapan menggunakan struktur statis dalam pemrograman C?
Seperti yang telah dicatat di sini, cara yang lebih baik untuk mencapai ini di C ++ mungkin menggunakan ruang nama. Tetapi karena tidak ada yang menyebutkan final
kata kunci di sini, saya memposting seperti apa persamaan langsung static class
dari C # di C ++ 11 atau lebih baru:
class BitParser final
{
public:
BitParser() = delete;
static bool GetBitAt(int buffer, int pos);
};
bool BitParser::GetBitAt(int buffer, int pos)
{
// your code
}
Anda 'dapat' memiliki kelas statis di C ++, seperti yang disebutkan sebelumnya, kelas statis adalah salah satu yang tidak memiliki objek yang dipakai. Dalam C ++, ini dapat diperoleh dengan mendeklarasikan konstruktor / destruktor sebagai pribadi. Hasil akhirnya sama.
Ini mirip dengan cara C # melakukannya di C ++
Dalam C # file.cs Anda dapat memiliki var pribadi di dalam fungsi publik. Ketika di file lain Anda dapat menggunakannya dengan memanggil namespace dengan fungsi seperti pada:
MyNamespace.Function(blah);
Berikut cara menerapkan hal yang sama di 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();
Tidak seperti bahasa pemrograman lain yang dikelola, "kelas statis" tidak memiliki arti dalam C ++. Anda dapat menggunakan fungsi anggota statis.
Satu kasus di mana ruang nama mungkin tidak begitu berguna untuk mencapai "kelas statis" adalah ketika menggunakan kelas-kelas ini untuk mencapai komposisi di atas warisan. Ruang nama tidak boleh menjadi teman kelas dan karenanya tidak dapat mengakses anggota pribadi kelas.
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;
}
};
Salah satu (dari banyak) alternatif, tetapi yang paling (menurut saya) anggun (dibandingkan dengan menggunakan ruang nama dan konstruktor pribadi untuk meniru perilaku statis), cara untuk mencapai perilaku "kelas yang tidak dapat dipakai" dalam C ++ adalah dengan mendeklarasikan fungsi virtual murni dummy dengan private
pengubah akses.
class Foo {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
Jika Anda menggunakan C ++ 11, Anda bisa bekerja lebih keras untuk memastikan bahwa kelas tidak diwarisi (untuk murni meniru perilaku kelas statis) dengan menggunakan final
specifier dalam deklarasi kelas untuk membatasi kelas lain dari mewarisi itu .
// C++11 ONLY
class Foo final {
public:
static int someMethod(int someArg);
private:
virtual void __dummy() = 0;
};
Meskipun terdengar konyol dan tidak masuk akal, C ++ 11 memungkinkan deklarasi "fungsi virtual murni yang tidak dapat ditimpa", yang dapat Anda gunakan bersama mendeklarasikan kelas final
untuk secara murni dan sepenuhnya menerapkan perilaku statis karena ini menghasilkan resultan kelas untuk tidak diwariskan dan fungsi dummy untuk tidak diganti dengan cara apa pun.
// 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