คุณจะสร้างคลาสแบบคงที่ใน C ++ ได้อย่างไร ฉันควรจะทำสิ่งที่ชอบ:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
สมมติว่าฉันสร้างBitParser
ชั้นเรียน BitParser
คำจำกัดความของคลาสจะเป็นอย่างไร
คุณจะสร้างคลาสแบบคงที่ใน C ++ ได้อย่างไร ฉันควรจะทำสิ่งที่ชอบ:
cout << "bit 5 is " << BitParser::getBitAt(buffer, 5) << endl;
สมมติว่าฉันสร้างBitParser
ชั้นเรียน BitParser
คำจำกัดความของคลาสจะเป็นอย่างไร
คำตอบ:
หากคุณกำลังมองหาวิธีการใช้คำหลัก "คงที่" กับคลาสเช่นคุณสามารถใน 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;
}
คุณสามารถใช้รหัสนี้เพื่อเรียกใช้วิธีการเช่นเดียวกับรหัสตัวอย่างของคุณ
หวังว่าจะช่วย! ไชโย
private: BitParser() {}
สิ่งนี้จะป้องกันไม่ให้ใครสร้างอินสแตนซ์
BitParser() = delete;
จะสื่อความตั้งใจที่จะลบนวกรรมิก (ไม่ใช่เพียงซ่อนไว้private
)
พิจารณาวิธีการแก้ปัญหาของแมตต์ราคา
สิ่งที่คุณต้องการคือแสดงในซีแมนทิกส์ 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 static method" เนื่องจาก:
บทสรุป: อย่าคัดลอก / วางรูปแบบของ 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 ยังคงมีอยู่!
ซึ่งแตกต่างจาก "ระดับคงที่" เดินบนเปลือยกับสมุดที่อยู่ของเธอมีรอยสักบนผิวหนังของเธอ "ไม่ระบุชื่อ" namespace ห่มอย่างเต็มที่ซึ่งดูเหมือนว่าค่อนข้างดีกว่าที่ห่อหุ้ม AFAIK
เว้นแต่ผู้ใช้รหัสของคุณต้องโทษ (ฉันจะให้คุณเช่นการออกกำลังกายพบว่าวิธีการหนึ่งที่สามารถเข้าถึงไปยังส่วนส่วนตัวของคลาสที่สาธารณะโดยใช้สับพฤติกรรมที่ไม่ได้กำหนดสกปรก ... ), สิ่งที่private
เป็นprivate
ถึงแม้ว่ามันจะ สามารถมองเห็นได้ในprivate
ส่วนของคลาสที่ประกาศในส่วนหัว
ถึงกระนั้นหากคุณต้องการเพิ่ม "ฟังก์ชั่นส่วนตัว" อีกอันที่สามารถเข้าถึงสมาชิกส่วนตัวคุณยังต้องประกาศให้ทุกคนในโลกเห็นด้วยการแก้ไขหัวข้อซึ่งเป็นความขัดแย้งเท่าที่ฉันกังวล: ถ้าฉันเปลี่ยนการใช้งานของ รหัสของฉัน (ส่วน CPP) จากนั้นส่วนต่อประสาน (ส่วน HPP) ไม่ควรเปลี่ยนแปลง การอ้างถึง Leonidas: " นี่คือการทำนาย! "
คลาสแบบสแตติกเมธอดจะดีกว่าเนมสเปซที่มีฟังก์ชั่นที่ไม่ใช่สมาชิกเมื่อใด
เมื่อคุณต้องการจัดกลุ่มฟังก์ชั่นร่วมกันและฟีดกลุ่มนั้นไปยังแม่แบบ
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 !!!
เนื่องจากถ้าคลาสสามารถเป็นพารามิเตอร์เทมเพลตเนมสเปซไม่สามารถทำได้
#define private public
ในส่วนหัว ... ^ _ ^ ...
utilities
namespace ด้วยวิธีนี้ฟังก์ชั่นนี้สามารถทดสอบหน่วยและยังไม่สามารถเข้าถึงสมาชิกส่วนตัว (ตามที่ได้รับเป็นพารามิเตอร์ในการเรียกใช้ฟังก์ชัน) ...
namespace
จะไม่สามารถเข้าถึงคุณได้ถึงglobal
แม้ว่าจะซ่อนสมาชิกก็ตาม เห็นได้ชัดว่าพวกเขาจะต้องเดา แต่ถ้าคุณตั้งใจทำให้งงงวยรหัสของคุณชื่อตัวแปรค่อนข้างง่ายที่จะคาดเดา
คุณยังสามารถสร้างฟังก์ชั่นฟรีใน namespace:
ใน 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
200 ครั้งจะเป็นสิ่งที่ดี
ใน C ++ คุณต้องการสร้างฟังก์ชันคงที่ของคลาส (ไม่ใช่คลาสแบบสแตติก)
class BitParser {
public:
...
static ... getBitAt(...) {
}
};
จากนั้นคุณควรจะสามารถเรียกใช้ฟังก์ชันโดยใช้ BitParser :: getBitAt () โดยไม่ต้องสร้างวัตถุที่ฉันคิดว่าเป็นผลลัพธ์ที่ต้องการ
ฉันสามารถเขียนบางอย่างได้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?
ตามที่ได้รับการบันทึกไว้ที่นี่วิธีที่ดีกว่าในการบรรลุสิ่งนี้ใน 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 ++ สิ่งนี้สามารถรับได้โดยการประกาศตัวสร้าง / destructor เป็นส่วนตัว ผลลัพธ์ที่ได้ก็เหมือนกัน
ใน C ++ ที่มีการจัดการไวยากรณ์คลาสคงที่คือ: -
public ref class BitParser abstract sealed
{
public:
static bool GetBitAt(...)
{
...
}
}
... มาสายดีกว่าไม่มาเลย...
ซึ่งคล้ายกับวิธีการทำ C # ใน C ++
ใน C # file.cs คุณสามารถมี var ส่วนตัวภายในฟังก์ชั่นสาธารณะ เมื่ออยู่ในไฟล์อื่นคุณสามารถใช้มันได้โดยการเรียก namespace พร้อมกับฟังก์ชั่นดังต่อไปนี้:
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 ++ คุณสามารถใช้ประโยชน์จากฟังก์ชั่นสมาชิกคงที่
กรณีหนึ่งที่เนมสเปซอาจไม่เป็นประโยชน์สำหรับการบรรลุ "คลาสคงที่" คือเมื่อใช้คลาสเหล่านี้เพื่อให้ได้องค์ประกอบมากกว่าการสืบทอด เนมสเปซไม่สามารถเป็นเพื่อนของชั้นเรียนได้และไม่สามารถเข้าถึงสมาชิกส่วนตัวของชั้นเรียนได้
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;
}
};
ทางเลือกหนึ่ง (จากหลาย ๆ ทาง) แต่ที่สุด (ในความคิดของฉัน) ที่สง่างาม (เมื่อเทียบกับการใช้เนมสเปซและคอนสตรัคเตอร์ส่วนตัวเพื่อเลียนแบบพฤติกรรมแบบคงที่) วิธีการบรรลุพฤติกรรม "คลาสที่ไม่สามารถสร้างอินสแตนซ์ ประกาศฟังก์ชั่นเสมือนจริงที่จำลองด้วย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