C ++: เข้าถึงสมาชิก const ผ่านคลาสหรืออินสแตนซ์?

ใน C ++ มีเหตุผลใดที่จะไม่เข้าถึงตัวแปรสมาชิกแบบคงที่ผ่านอินสแตนซ์ของคลาสหรือไม่ ฉันรู้ว่า Java ขมวดคิ้วกับเรื่องนี้และสงสัยว่ามันสำคัญกับ C ++ หรือไม่ ตัวอย่าง:

class Foo {
  static const int ZERO = 0;
  static const int ONE = 1;
  ...
};


void bar(const Foo& inst) {
   // is this ok?
   int val1 = inst.ZERO;
   // or should I prefer:
   int val2 = Foo::ZERO
   ...
};

ฉันมีคำถามที่สองโบนัส ถ้าฉันประกาศ static double ฉันต้องกำหนดมันที่ไหนสักแห่งและคำจำกัดความนั้นจะต้องทำซ้ำประเภทนั้น เหตุใดจึงต้องพิมพ์ซ้ำ? ตัวอย่างเช่น:

In a header:
  class Foo {
    static const double d;
  };
In a source file:
  const double Foo::d = 42;

เหตุใดฉันจึงต้องทำซ้ำส่วน "const double" ในไฟล์ cpp ของฉัน


person criddell    schedule 06.08.2009    source แหล่งที่มา
comment
ไม่ซ้ำกันทุกประการ แต่ฉันคิดว่าอาจมีคำตอบเดียวกัน: stackoverflow.com/questions/840522/   -  person Fred Larson    schedule 07.08.2009
comment
ใช่. อ่านคำตอบของ Adam Rosenthal ด้วยเหตุผลที่ดี มาก ว่าทำไมคุณควรเลือกคลาสเป็นคำนำหน้าแทนที่จะเป็นตัวอย่าง   -  person quark    schedule 07.08.2009


คำตอบ (6)


สำหรับคำถามแรก นอกเหนือจากเรื่องของสไตล์ (ทำให้เห็นได้ชัดว่ามันเป็นตัวแปรคลาสและไม่มีอ็อบเจ็กต์ที่เกี่ยวข้อง) Fred Larsen แสดงความคิดเห็นต่อคำถาม โดยอ้างอิงถึงคำถามก่อนหน้า อ่าน คำตอบของ Adam Rosenthal สำหรับเหตุผลที่ดี มาก ว่าทำไมคุณจึงต้องระวังเรื่องนี้ (ฉันจะโหวตให้ Fred ถ้าเขาโพสต์มันเป็นคำตอบ แต่ฉันไม่สามารถให้เครดิตได้ว่าจะถึงกำหนดส่ง ฉันโหวตให้กับ Adam)

สำหรับคำถามที่สองของคุณ:

เหตุใดฉันจึงต้องทำซ้ำส่วน "const double" ในไฟล์ cpp ของฉัน

คุณต้องทำซ้ำประเภทดังกล่าวเป็นรายละเอียดการใช้งานเป็นหลัก นั่นคือวิธีที่คอมไพเลอร์ C++ แยกวิเคราะห์การประกาศ สิ่งนี้ไม่เหมาะอย่างยิ่งสำหรับตัวแปรท้องถิ่นเช่นกัน และ C++1x (เดิมคือ C++0x) ใช้คีย์เวิร์ด auto เพื่อหลีกเลี่ยงความจำเป็นในการทำซ้ำสำหรับตัวแปรฟังก์ชันปกติ

ดังนั้นสิ่งนี้:

vector<string> v;
vector<string>::iterator it = v.begin();

สามารถเป็นสิ่งนี้ได้:

vector<string> v;
auto it = v.begin();

ไม่มีเหตุผลที่ชัดเจนว่าทำไมสิ่งนี้ถึงใช้งานแบบคงที่ไม่ได้เช่นกัน ดังนั้นในกรณีของคุณ:

const double Foo::d = 42;

ก็สามารถกลายเป็นสิ่งนี้ได้

static Foo::d = 42;

กุญแจสำคัญคือการมี บาง วิธีในการระบุสิ่งนี้ว่าเป็นการประกาศ

หมายเหตุ ฉันบอกว่าไม่มีเหตุผล ชัดเจน: ไวยากรณ์ของ C++ นั้นเป็นตำนานที่มีชีวิต: มันอย่างยิ่งยากที่จะครอบคลุม Edge Case ทั้งหมด ฉัน คิดว่า ข้อความข้างต้นไม่คลุมเครือ แต่อาจเป็นเช่นนั้น หากไม่เป็นเช่นนั้นพวกเขาสามารถเพิ่มสิ่งนั้นลงในภาษาได้ บอกพวกเขาเกี่ยวกับมัน ... สำหรับ C++2x :/.

person quark    schedule 06.08.2009

ฉันชอบ Foo::ZERO มากกว่า inst.ZERO เพราะจะทำให้ชัดเจนยิ่งขึ้นว่าเกิดอะไรขึ้น อย่างไรก็ตาม ในวิธีการของคลาส Foo ฉันจะใช้ ZERO

สำหรับคำถามพิเศษนั้น const เป็นส่วนหนึ่งของประเภทที่สมบูรณ์เท่านั้น

person Reunanen    schedule 06.08.2009
comment
ฉันเข้าใจว่า const เป็นส่วนหนึ่งของประเภท ฉันสงสัยว่าทำไมฉันต้องทำซ้ำประเภทเลย ฉันอยากจะพูดว่า: Foo::d = 42; มีการประกาศ Foo::d เพียงรายการเดียวในไฟล์ส่วนหัว สิ่งนี้ทำให้เกิดความคลุมเครือบางอย่างที่ฉันไม่เห็นที่นี่หรือไม่ มีฟังก์ชั่นสมาชิกชื่อ d ได้ไหม? ฉันรู้ว่ามีเหตุผลที่ดีในการทำซ้ำประเภทนี้ ฉันแค่ไม่รู้ว่ามันคืออะไร - person criddell; 07.08.2009
comment
บางทีนั่นอาจเป็นเพียงกฎของภาษา? - person Juan; 07.08.2009
comment
ฉันพยายามอธิบายสิ่งนี้ในความคิดเห็น แต่ห้องหมด (ถอนหายใจ) ฉันตอบมันด้านล่าง - person quark; 07.08.2009

เนื่องจากคุณกำลังประกาศว่าเป็นค่าคงที่และทำให้เป็นค่าคงที่ของคลาส ฉันจะใช้ Foo::Zero เพื่อสื่อสารเจตนาไปยังผู้อ่านโค้ดของคุณที่ไม่เป็นทางการและไม่ไม่เป็นทางการ

ฉันจะแทนที่ชื่อตัวพิมพ์ใหญ่ทั้งหมดของค่าคงที่ด้วย เช่น เปลี่ยน Foo::ZERO เป็น Foo::Zero แบบแผนปกติสำหรับแมโครตัวประมวลผลล่วงหน้าคือการตั้งชื่อให้เป็นตัวพิมพ์ใหญ่ทั้งหมด และการใช้รูปแบบการตั้งชื่อที่คล้ายกันสำหรับค่าคงที่ C++ ของคุณกำลังถามถึงปัญหา เนื่องจากตัวประมวลผลล่วงหน้าอาจมองข้ามค่าคงที่ C++ ของคุณ และคุณจะพบข้อความแสดงข้อผิดพลาดที่น่าสนใจมาก

person Timo Geusch    schedule 06.08.2009
comment
ว้าว- จุดดีในการตั้งชื่อแบบแผน จริงๆ ฉันโดนสิ่งนี้มากกว่าหนึ่งครั้งแล้ว (ได้โปรดทุกคน-- หยุด #define'ing PI!!!) - person criddell; 07.08.2009

ฉันจะใช้ Foo::ZERO แต่นั่นเป็นเพียงฉัน โดยเฉพาะอย่างยิ่งถ้าคุณมาจาก Foo ซึ่งทำให้สับสน

สำหรับคำถามที่สอง คุณต้องสร้างหน่วยความจำสำหรับค่าสองเท่าและเกิดขึ้นในหน่วยการใช้งาน

ฉันคิดว่าประเภทเดียวที่คุณไม่จำเป็นต้องสร้างหน่วยความจำคือประเภทอินทิกรัลแบบ const จากนั้นคุณสามารถใส่ค่าลงในไฟล์ส่วนหัวได้ แต่เนื่องจากไม่มีที่อยู่ คุณจึงไม่สามารถส่งผ่านโดยการอ้างอิงถึงฟังก์ชันได้ เว้นแต่คุณจะใส่คำจำกัดความในไฟล์ .cpp (นี่น่าจะเป็นวิธีการทำงานกับ gcc)

person Juan    schedule 06.08.2009

ไม่สำคัญว่าคุณจะใช้รูปแบบใดในตัวอย่างของคุณ พวกเขาทั้งสองหมายถึงสิ่งเดียวกัน ฉันต้องการใช้วิธีเรียนโดยทั่วไปเพราะคุณอาจไม่มีอินสแตนซ์ที่สะดวกในการใช้ตัวดำเนินการจุดเสมอไป

การมีทั้งสองตัวเลือกเป็นเรื่องดีหากคุณพิจารณาให้มีคนเขียนฟังก์ชันเทมเพลต พวกเขาอาจเข้ารหัสฟังก์ชันด้วยตัวดำเนินการจุด คลาสของคุณที่มีสมาชิกคลาสแบบคงที่ยังคงสามารถใช้เพื่อสร้างอินสแตนซ์เทมเพลตได้เนื่องจากรองรับไวยากรณ์นั้น

สำหรับคำถามพิเศษของคุณ นั่นเป็นเพียงวิธีการใช้ภาษา คุณต้องประกาศประเภทเต็มเสมอ คุณควรถามคำถามนั้นแยกกัน

person Brian Neal    schedule 06.08.2009

โดยส่วนตัวแล้วฉันจะใช้ enum ที่ไม่ระบุชื่อ ผลลัพธ์ที่ได้ก็เหมือนกันทุกประการ :)

สำหรับคำถามของคุณ ฉันชอบ Foo::Zero มากกว่าเพราะมันชัดเจนจากการดูว่าคุณกำลังเข้าถึงอะไร inst.Zero ต้องการให้คุณพิจารณาว่า inst คืออะไรก่อนถึงมือ

คุณต้องทำซ้ำประเภทข้อมูลเพราะนั่นคือวิธีการทำงานของ C ++ ในทำนองเดียวกันหากคุณเขียนสิ่งต่อไปนี้ในไฟล์ส่วนหัว

extern int foo;

คุณจะต้องพูดถึง

int foo

ในไฟล์ CPP ดังที่ pukku พูดถึงคุณกำลังประกาศตัวแปรประเภท "const int" ดังนั้นจะต้องทำซ้ำ "const int" ในคำจำกัดความของตัวแปร

person Goz    schedule 06.08.2009
comment
enum ที่ไม่ระบุชื่อบังคับให้มีขนาดพื้นที่เก็บข้อมูลเฉพาะ (int) หากคุณต้องการให้ค่าคงที่เป็นประเภทอื่น คุณจะไม่สามารถใช้การแจงนับที่ไม่ระบุตัวตนได้ - person Chris Cleeland; 07.08.2009