คลาสที่ซ้อนกันนั้นเหมือนกับคลาสปกติ แต่:
- พวกเขามีข้อ จำกัด การเข้าถึงเพิ่มเติม (ตามคำจำกัดความทั้งหมดภายในคำจำกัดความของคลาส)
- พวกเขาไม่สร้างมลภาวะให้กับ namespace ที่กำหนดเช่นเนมสเปซส่วนกลาง หากคุณรู้สึกว่าคลาส B นั้นเชื่อมโยงอย่างลึกซึ้งกับคลาส A แต่วัตถุของ A และ B นั้นไม่จำเป็นต้องเกี่ยวข้องกันดังนั้นคุณอาจต้องการให้คลาส B เข้าถึงได้ผ่านการกำหนดขอบเขตคลาส A เท่านั้น (มันจะเรียกว่า A :: ชั้น)
ตัวอย่างบางส่วน:
คลาสซ้อนแบบพับลิกเพื่อวางไว้ในขอบเขตของคลาสที่เกี่ยวข้อง
สมมติว่าคุณต้องการที่จะได้เรียนซึ่งจะรวบรวมวัตถุของคลาสSomeSpecificCollection
Element
จากนั้นคุณสามารถ:
ประกาศสองคลาส: SomeSpecificCollection
และElement
- ไม่ดีเนื่องจากชื่อ "องค์ประกอบ" เป็นชื่อทั่วไปเพียงพอที่จะทำให้เกิดการปะทะกันของชื่อที่เป็นไปได้
แนะนำ namespace someSpecificCollection
และประกาศการเรียนและsomeSpecificCollection::Collection
someSpecificCollection::Element
ไม่มีความเสี่ยงจากการปะทะกันของชื่อ แต่จะได้รับรายละเอียดเพิ่มเติมอีกหรือไม่
ประกาศคลาสระดับโลกสองคลาสSomeSpecificCollection
และSomeSpecificCollectionElement
- ซึ่งมีข้อเสียเล็กน้อย แต่อาจโอเค
ประกาศคลาสระดับโลกSomeSpecificCollection
และคลาสElement
เป็นคลาสที่ซ้อนกัน แล้ว:
- คุณไม่เสี่ยงต่อการปะทะกันของชื่อใด ๆ เนื่องจากองค์ประกอบไม่ได้อยู่ในเนมสเปซส่วนกลาง
- ในการใช้งานของ
SomeSpecificCollection
คุณหมายถึงเพียงElement
และทุกที่อื่น ๆSomeSpecificCollection::Element
- ซึ่งมีลักษณะ + - เช่นเดียวกับ 3 แต่ชัดเจนยิ่งขึ้น
- มันเรียบง่ายธรรมดาว่า "องค์ประกอบของคอลเลกชันเฉพาะ" ไม่ใช่ "องค์ประกอบเฉพาะของคอลเลกชัน"
- มันสามารถมองเห็นได้ว่า
SomeSpecificCollection
เป็นชั้นเรียนด้วย
ในความคิดของฉันตัวแปรสุดท้ายคือแน่นอนที่สุดใช้งานง่ายและดังนั้นจึงออกแบบที่ดีที่สุด
ให้ฉันเครียด - มันไม่แตกต่างกันมากจากการสร้างสองระดับโลกที่มีชื่อ verbose เพิ่มเติม มันเป็นรายละเอียดเล็ก ๆ น้อย ๆ แต่ imho ทำให้รหัสชัดเจนยิ่งขึ้น
แนะนำขอบเขตอื่นภายในขอบเขตของคลาส
สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับการแนะนำ typedefs หรือ enums ฉันจะโพสต์ตัวอย่างรหัสที่นี่:
class Product {
public:
enum ProductType {
FANCY, AWESOME, USEFUL
};
enum ProductBoxType {
BOX, BAG, CRATE
};
Product(ProductType t, ProductBoxType b, String name);
// the rest of the class: fields, methods
};
หนึ่งแล้วจะโทร:
Product p(Product::FANCY, Product::BOX);
แต่เมื่อดูที่ข้อเสนอการทำโค้ดให้เสร็จสมบูรณ์Product::
เรามักจะได้รับค่า enum ที่เป็นไปได้ทั้งหมด (BOX, FANCY, CRATE) และมันง่ายที่จะทำผิดพลาดได้ที่นี่ (C ++ 0x พิมพ์อย่างยิ่ง enums ชนิดของการแก้ปัญหา )
แต่ถ้าคุณแนะนำขอบเขตเพิ่มเติมสำหรับ enums เหล่านั้นโดยใช้คลาสที่ซ้อนกันสิ่งต่าง ๆ อาจมีลักษณะดังนี้:
class Product {
public:
struct ProductType {
enum Enum { FANCY, AWESOME, USEFUL };
};
struct ProductBoxType {
enum Enum { BOX, BAG, CRATE };
};
Product(ProductType::Enum t, ProductBoxType::Enum b, String name);
// the rest of the class: fields, methods
};
จากนั้นการโทรจะมีลักษณะดังนี้:
Product p(Product::ProductType::FANCY, Product::ProductBoxType::BOX);
จากนั้นโดยการพิมพ์Product::ProductType::
ใน IDE หนึ่งจะได้รับเพียง enums จากขอบเขตที่ต้องการแนะนำ นอกจากนี้ยังช่วยลดความเสี่ยงในการทำผิดพลาด
หลักสูตรนี้อาจไม่จำเป็นสำหรับชั้นเรียนขนาดเล็ก แต่หากมีจำนวนมาก enums แล้วมันจะทำให้สิ่งที่ง่ายขึ้นสำหรับโปรแกรมเมอร์ไคลเอนต์
ในทำนองเดียวกันคุณสามารถ "จัดระเบียบ" กลุ่มข้อความขนาดใหญ่ในเทมเพลตหากคุณต้องการ มันเป็นรูปแบบที่มีประโยชน์บางครั้ง
PIMPL สำนวน
PIMPL (ย่อมาจาก Pointer to IMPLementation) เป็นสำนวนที่มีประโยชน์ในการลบรายละเอียดการใช้งานของคลาสออกจากส่วนหัว สิ่งนี้จะช่วยลดความต้องการในการคอมไพล์คลาสใหม่โดยขึ้นอยู่กับส่วนหัวของคลาสเมื่อใดก็ตามที่ส่วน "การใช้งาน" ของการเปลี่ยนแปลงส่วนหัว
มันมักจะนำมาใช้โดยใช้ชั้นซ้อนกัน:
Xh:
class X {
public:
X();
virtual ~X();
void publicInterface();
void publicInterface2();
private:
struct Impl;
std::unique_ptr<Impl> impl;
}
X.cpp:
#include "X.h"
#include <windows.h>
struct X::Impl {
HWND hWnd; // this field is a part of the class, but no need to include windows.h in header
// all private fields, methods go here
void privateMethod(HWND wnd);
void privateMethod();
};
X::X() : impl(new Impl()) {
// ...
}
// and the rest of definitions go here
สิ่งนี้มีประโยชน์อย่างยิ่งหากนิยามคลาสแบบเต็มต้องการนิยามชนิดจากไลบรารีภายนอกบางตัวที่มีไฟล์ส่วนหัวที่หนักหรือน่าเกลียด (รับ WinAPI) ถ้าคุณใช้ PIMPL แล้วคุณสามารถแนบใด ๆ การทำงาน WinAPI เฉพาะเฉพาะในและไม่เคยรวมไว้ใน.cpp
.h