การมีออบเจ็กต์หนึ่งจะ จำกัด สิ่งที่คุณสามารถทำได้และสิ่งที่คอมไพเลอร์สามารถทำได้โดยไม่ต้องจ่ายผลตอบแทนมากนัก
คลาสรูททั่วไปทำให้สามารถสร้าง container-of-nothing และแยกสิ่งที่พวกมันมีdynamic_cast
แต่ถ้าคุณต้องการ container-of-nothing อะไรก็คล้ายกับที่จะboost::any
ทำได้โดยไม่มีคลาสรูททั่วไป และboost::any
ยังรองรับพื้นฐาน - มันยังสามารถรองรับการเพิ่มประสิทธิภาพบัฟเฟอร์ขนาดเล็กและปล่อยให้พวกเขาเกือบ "unboxed" ในสำนวนภาษาจาวา
C ++ รองรับและเจริญเติบโตในประเภทของค่า ทั้งตัวอักษรและโปรแกรมเมอร์ประเภทค่าเขียน คอนเทนเนอร์ C ++ จัดเก็บเรียงลำดับแฮชบริโภคและผลิตประเภทมูลค่าอย่างมีประสิทธิภาพ
การสืบทอดโดยเฉพาะอย่างยิ่งชนิดของคลาสฐานสไตล์ Java ที่ถ่ายทอดทางพันธุกรรมหมายความว่าต้องใช้ "ตัวชี้" หรือ "อ้างอิง" ประเภทร้านค้าฟรี หมายเลขอ้างอิง / ตัวชี้ / การอ้างอิงถึงข้อมูลของคุณมีตัวชี้ไปยังอินเทอร์เฟซของคลาสและ polymorphically สามารถแสดงสิ่งอื่นได้
แม้ว่าสิ่งนี้จะมีประโยชน์ในบางสถานการณ์เมื่อคุณแต่งงานกับรูปแบบที่มี "คลาสพื้นฐานทั่วไป" คุณได้ล็อคฐานรหัสทั้งหมดของคุณเป็นค่าใช้จ่ายและน้ำหนักกระเป๋าของรูปแบบนี้แม้ว่าจะไม่มีประโยชน์ก็ตาม
เกือบทุกครั้งที่คุณรู้เกี่ยวกับประเภทมากกว่า "เป็นวัตถุ" ที่ไซต์การโทรหรือในรหัสที่ใช้
ถ้าฟังก์ชั่นนั้นง่ายการเขียนฟังก์ชั่นเป็นเทมเพลตจะช่วยให้คุณมีรูปแบบการรวบรวมตามเวลาที่หลากหลายซึ่งข้อมูลในไซต์ที่เรียกไม่ถูกโยนทิ้งไป หากฟังก์ชันมีความซับซ้อนมากขึ้นการลบประเภทสามารถทำได้โดยการดำเนินการแบบสม่ำเสมอในประเภทที่คุณต้องการดำเนินการ (เช่นการทำให้เป็นอนุกรมและการลดทอนความเป็นไปได้) สามารถสร้างและจัดเก็บได้ (ณ เวลารวบรวม) รหัสในหน่วยการแปลอื่น
สมมติว่าคุณมีห้องสมุดบางแห่งที่คุณต้องการให้ทุกอย่างเป็นอนุกรม วิธีหนึ่งคือการมีคลาสฐาน:
struct serialization_friendly {
virtual void write_to( my_buffer* ) const = 0;
virtual void read_from( my_buffer const* ) = 0;
virtual ~serialization_friendly() {}
};
serialization_friendly
ตอนนี้บิตของรหัสทุกครั้งที่คุณสามารถเขียน
void serialize( my_buffer* b, serialization_friendly const* x ) {
if (x) x->write_to(b);
}
ยกเว้นไม่ใช่กstd::vector
ดังนั้นตอนนี้คุณต้องเขียนทุกคอนเทนเนอร์ และไม่ใช่จำนวนเต็มที่คุณได้รับจากห้องสมุด bignum นั้น และไม่ใช่ประเภทที่คุณเขียนว่าคุณไม่คิดว่าจำเป็นต้องมีการทำให้เป็นอนุกรม และไม่ได้เป็นtuple
หรือint
หรือหรือdouble
std::ptrdiff_t
เราใช้แนวทางอื่น:
void write_to( my_buffer* b, int x ) {
b->write_integer(x);
}
template<class T,
class=std::enable_if_t< void_t<
std::declval<T const*>()->write_to( std::declval<my_buffer*>()
> >
>
void write_to( my_buffer* b, T const* x ) {
if (x) x->write_to(b);
}
template<class T>
void serialize( my_buffer* b, T const& t ) {
write_to( b, t );
}
ซึ่งประกอบไปด้วยดีไม่ทำอะไรเลยดูเหมือน ยกเว้นตอนนี้เราสามารถขยายได้write_to
โดยการแทนที่write_to
เป็นฟังก์ชันฟรีในเนมสเปซของประเภทหรือวิธีการในประเภท
เรายังสามารถเขียนโค้ดการลบประเภท:
namespace details {
struct can_serialize_pimpl {
virtual void write_to( my_buffer* ) const = 0;
virtual void read_from( my_buffer const* ) = 0;
virtual ~can_serialize_pimpl() {}
};
}
struct can_serialize {
void write_to( my_buffer* b ) const { pImpl->write_to(b); }
void read_from( my_buffer const* b ) { pImpl->read_from(b); }
std::unique_ptr<details::can_serialize_pimpl> pImpl;
template<class T> can_serialize(T&&);
};
namespace details {
template<class T>
struct can_serialize : can_serialize_pimpl {
std::decay_t<T>* t;
void write_to( my_buffer*b ) const final override {
serialize( b, std::forward<T>(*t) );
}
void read_from( my_buffer const* ) final override {
deserialize( b, std::forward<T>(*t) );
}
can_serialize(T&& in):t(&in) {}
};
}
template<class T> can_serialize::can_serialize<T>(T&&t):pImpl(
std::make_unique<details::can_serialize<T>>( std::forward<T>(t) );
) {}
และตอนนี้เราสามารถนำประเภทใดก็ได้และใส่เข้าไปในcan_serialize
อินเทอร์เฟซที่ช่วยให้คุณสามารถเรียกใช้serialize
ในภายหลังผ่านอินเตอร์เฟสเสมือน
ดังนั้น:
void writer_thingy( can_serialize s );
เป็นฟังก์ชันที่ใช้ทุกสิ่งที่สามารถทำให้เป็นอันดับแทน
void writer_thingy( serialization_friendly const* s );
และครั้งแรกซึ่งแตกต่างจากที่สองก็สามารถจัดการint
ได้std::vector<std::vector<Bob>>
โดยอัตโนมัติ
มันใช้เวลาไม่นานในการเขียนโดยเฉพาะอย่างยิ่งเพราะสิ่งนี้เป็นสิ่งที่คุณไม่ค่อยอยากทำ แต่เราได้รับความสามารถในการรักษาสิ่งต่าง ๆ ให้เป็นอนุกรมโดยไม่ต้องใช้ประเภทฐาน
มีอะไรมากกว่าที่ตอนนี้เราสามารถทำให้std::vector<T>
serializable ในฐานะพลเมืองชั้นแรกโดยเพียงแค่การเอาชนะwrite_to( my_buffer*, std::vector<T> const& )
ด้วย - เกินนั้นก็สามารถส่งผ่านไปยังcan_serialize
และ serializabilty ของstd::vector
ได้รับการจัดเก็บไว้ใน vtable .write_to
และเข้าถึงได้โดย
ในระยะสั้น C ++ มีพลังมากพอที่คุณจะสามารถใช้ประโยชน์จากคลาสฐานเดียวแบบทันทีเมื่อต้องการโดยไม่ต้องจ่ายราคาของลำดับชั้นการสืบทอดที่บังคับเมื่อไม่จำเป็น และเวลาที่จำเป็นต้องใช้ฐานเดียว (แกล้งหรือไม่) นั้นหายาก
เมื่อประเภทเป็นตัวตนของพวกเขาและคุณรู้ว่าพวกเขาคืออะไรโอกาสในการปรับให้เหมาะสมนั้นมีอยู่มากมาย ข้อมูลถูกเก็บไว้ในเครื่องและต่อเนื่องกัน (ซึ่งเป็นสิ่งสำคัญอย่างยิ่งสำหรับแคชที่เป็นมิตรกับโปรเซสเซอร์ที่ทันสมัย) คอมไพเลอร์สามารถเข้าใจได้อย่างง่ายดายว่าการดำเนินการที่กำหนดไว้คืออะไร (แทนที่จะมีตัวชี้เมธอดเสมือนทึบแสงที่ต้องกระโดดข้าม อีกด้านหนึ่ง) ซึ่งช่วยให้สามารถเรียงลำดับคำสั่งได้ดีที่สุดและตอกหมุดลงไปในรูกลม