ประกาศ enum ภายในคลาส


151

ในโค้ดต่อไปนี้Colorenum จะถูกประกาศภายในCarคลาสเพื่อ จำกัด ขอบเขตของ enum และพยายามที่จะไม่ "สร้างมลภาวะ" เนมสเปซส่วนกลาง

class Car
{
public:

   enum Color
   {
      RED,
      BLUE,
      WHITE
   };

   void SetColor( Car::Color color )
   {
      _color = color;
   }

   Car::Color GetColor() const
   {
      return _color;
   }

private:

   Car::Color _color;

};

(1) นี่เป็นวิธีที่ดีในการ จำกัด ขอบเขตของColorenum หรือไม่? หรือฉันควรประกาศมันนอกCarชั้นเรียน แต่อาจจะอยู่ในเนมสเปซหรือ struct ของตัวเอง? ฉันเพิ่งมาข้ามบทความนี้ในวันนี้ซึ่งสนับสนุนหลังและกล่าวถึงจุดที่ดีบางอย่างเกี่ยวกับ enums: http://gamesfromwithin.com/stupid-c-tricks-2-better-enums

(2) ในตัวอย่างนี้เมื่อทำงานภายในชั้นเรียนจะดีที่สุดในการเขียนรหัส enum เป็นCar::Colorหรือแค่Colorพอเพียง? (ฉันถือว่าอดีตนั้นดีกว่าในกรณีที่มีColorenum อื่นที่ประกาศใน namespace ทั่วโลกด้วยวิธีนี้อย่างน้อยที่สุดเราก็ชัดเจนเกี่ยวกับ enum ที่เรากำลังอ้างอิง)

คำตอบ:


86
  1. หากColorเป็นสิ่งที่เฉพาะเจาะจงเพียงแค่Carนั้นเป็นวิธีที่คุณจะ จำกัด ขอบเขต หากคุณกำลังจะมีColorenum อื่นที่คลาสอื่นใช้คุณก็อาจทำให้เป็นโกลบอล (หรืออย่างน้อยก็ภายนอกCar)

  2. มันไม่ต่างอะไรเลย หากมีทั่วโลกแล้วท้องถิ่นจะยังคงใช้อยู่เพราะใกล้ชิดกับขอบเขตปัจจุบัน โปรดทราบว่าหากคุณกำหนดฟังก์ชั่นเหล่านั้นนอกข้อกำหนดของคลาสคุณจะต้องระบุอย่างชัดเจนCar::Colorในอินเทอร์เฟซของฟังก์ชัน


12
2. ใช่และไม่ใช่ Car::Color getColor()แต่void Car::setColor(Color c)เนื่องจากในตัวsetColorเรามีตัวระบุอยู่แล้ว
Matthieu M.


66

ฉันชอบวิธีการดังต่อไปนี้ (รหัสด้านล่าง) มันแก้ปัญหา "เนมสเปซมลพิษ" แต่ก็เป็นประเภทที่ปลอดภัยกว่า (คุณไม่สามารถกำหนดและเปรียบเทียบจำนวนที่แตกต่างกันสองรายการหรือการแจงนับของคุณกับประเภทอื่น ๆ ในตัว ฯลฯ )

struct Color
{
    enum Type
    {
        Red, Green, Black
    };
    Type t_;
    Color(Type t) : t_(t) {}
    operator Type () const {return t_;}
private:
   //prevent automatic conversion for any other built-in types such as bool, int, etc
   template<typename T>
    operator T () const;
};

การใช้งาน:

Color c = Color::Red;
switch(c)
{
   case Color::Red:
     //некоторый код
   break;
}
Color2 c2 = Color2::Green;
c2 = c; //error
c2 = 3; //error
if (c2 == Color::Red ) {} //error
If (c2) {} error

ฉันสร้างมาโครเพื่อความสะดวกในการใช้งาน:

#define DEFINE_SIMPLE_ENUM(EnumName, seq) \
struct EnumName {\
   enum type \
   { \
      BOOST_PP_SEQ_FOR_EACH_I(DEFINE_SIMPLE_ENUM_VAL, EnumName, seq)\
   }; \
   type v; \
   EnumName(type v) : v(v) {} \
   operator type() const {return v;} \
private: \
    template<typename T> \
    operator T () const;};\

#define DEFINE_SIMPLE_ENUM_VAL(r, data, i, record) \
    BOOST_PP_TUPLE_ELEM(2, 0, record) = BOOST_PP_TUPLE_ELEM(2, 1, record),

การใช้งาน:

DEFINE_SIMPLE_ENUM(Color,
             ((Red, 1))
             ((Green, 3))
             )

อ้างอิงบางส่วน:

  1. Herb Sutter, Jum Hyslop, วารสารผู้ใช้ C / C ++, 22 (5), พฤษภาคม 2004
  2. สมุนไพรซัทเทอร์เดวิดอีมิลเลอร์ Bjarne Stroustrup Enums (ฉบับที่ 3) พิมพ์อย่างยิ่งกรกฏาคม 2550

ฉันชอบสิ่งนี้. นอกจากนี้ยังบังคับให้ enum ถูกสร้างอินสแตนซ์ด้วยค่าที่ถูกต้อง ฉันคิดว่าผู้ประกอบการที่ได้รับมอบหมายและตัวสร้างสำเนาจะมีประโยชน์ นอกจากนี้ t_ ควรเป็นแบบส่วนตัวด้วย มาโครที่ฉันสามารถทำได้โดยไม่ต้อง
jmucchiello

ฉันก็ชอบแบบนี้ ขอบคุณสำหรับการอ้างอิง
anio

1
คุณพูดว่า: "ยังเป็นประเภทที่ปลอดภัยกว่าอีกมาก (คุณไม่สามารถมอบหมายและเปรียบเทียบการแจกแจงที่ต่างกันสองแบบ ... "ทำไมคุณถึงคิดว่ามันเป็นคุณสมบัติที่ดีฉันคิดว่าif(c2 == Color::Red )มีเหตุผลและต้องรวบรวม แต่ในตัวอย่างของคุณ ไม่โต้แย้งเหมือนกันสำหรับการมอบหมายเช่นกัน
Nawaz

3
@Nawaz c2เป็นประเภทอื่น ( Color2) ดังนั้นทำไมคุณคิดc2 == Color::Redและมอบหมายให้รวบรวม? เกิดอะไรขึ้นถ้าColor::Redเป็น 1 และColor2::Red2 คืออะไร ควรColor::Red == Color2::Redประเมินtrueหรือfalse? หากคุณผสมตัวแจงนับแบบไม่ปลอดภัยคุณจะมีช่วงเวลาที่เลวร้าย
Victor K

2
ทำไมไม่พิมพ์ t_; เอกชน?
Zingam

7

โดยทั่วไปแล้วผมมักจะใส่ enums structของฉันใน ฉันได้เห็นแนวทางหลายประการรวมถึง "คำนำหน้า"

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

คิดอยู่เสมอว่าสิ่งนี้ดูเป็นCแนวทางมากกว่าC++คนอื่น (อย่างใดอย่างหนึ่งเนื่องจากตัวย่อและเนื่องจากเนมสเปซในC++)

ดังนั้นเพื่อ จำกัด ขอบเขตเรามีสองทางเลือก:

  • namespaces
  • structs / ชั้นเรียน

ฉันมักจะใช้structเพราะมันสามารถใช้เป็นพารามิเตอร์สำหรับการเขียนโปรแกรมแม่แบบในขณะที่ namespace ไม่สามารถจัดการได้

ตัวอย่างของการจัดการรวมถึง:

template <class T>
size_t number() { /**/ }

ซึ่งส่งกลับจำนวนองค์ประกอบของ enum ภายใน struct T:)


3

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.