Object Slicing (การตัดส่วนของออบเจกต์) เป็นปัญหาคลาสสิกใน C++ ที่เกิดขึ้นเมื่อมอบออบเจกต์ของคลาสลูก (Derived Class) ให้กับตัวแปรของคลาสแม่ (Base Class) แบบ Call by Value ทำให้สมาชิกข้อมูลและเมธอดที่เฉพาะเจาะจงของคลาสลูกถูก "ตัดทิ้ง" ไป เหลือเฉพาะส่วนของ Base Class เท่านั้น
ตัวอย่างปัญหา
class Foo {
int a;
};
class Bar : public Foo {
int b; // สมาชิกเพิ่มเติมของ Derived Class
};
เมื่อทำการ Assign:
Bar bar;
Foo foo = bar; // Object Slicing เกิดขึ้นที่นี่!
ตัวแปร foo จะมีเพียงสมาชิก a เท่านั้น ส่วน b ของ bar จะหายไป นี่คือที่มาของคำว่า "Slicing" (การหั่น/ตัด)
เหตุผลทางเทคนิค
C++ ใช้ Static Binding สำหรับการกำหนดขนาดหน่วยความจำของออบเจกต์ขนาดคงที่ (Value Semantics) เมื่อประกาศ Foo foo คอมไพเลอร์จัดสรรหน่วยความจำเพียงพอสำหรับ Foo เท่านั้น การคัดลอก Bar (ซึ่งใหญ่กว่า) เข้าไปในพื้นที่ของ Foo จึงทำให้ข้อมูลส่วนเกินถูกตัดทิ้ง
วิธีป้องกัน
- ใช้ Pointer หรือ Reference: เก็บออบเจกต์ผ่าน
Base*หรือBase&เพื่อรักษา Polymorphism - ใช้ Smart Pointers:
std::unique_ptr<Base>หรือstd::shared_ptr<Base> - ทำให้ Base Class เป็น Abstract: เพิ่ม Pure Virtual Function เพื่อบังคับให้สร้างออบเจกต์ผ่าน Pointer เท่านั้น
- ลบ Copy Constructor/Assignment Operator ของ Base: ใช้
= deleteเพื่อป้องกันการ Slicing โดยตรง
ตัวอย่างการแก้ไขด้วย Pointer
Bar bar;
Foo* foo_ptr = &bar; // ถูกต้อง: ไม่มี Slicing
foo_ptr->virtual_function(); // เรียกใช้งานของ Bar ได้ถูกต้อง
สรุป
Object Slicing เป็นพฤติกรรมที่ออกแบบมาของ C++ สำหรับ Value Semantics ไม่ใช่ Bug แต่นักพัฒนาต้องเข้าใจและระวังการใช้งาน Pass by Value กับคลาสที่มี Inheritance Hierarchy ควรใช้ Pointer/Reference ร่วมกับ Virtual Functions เพื่อให้ได้ Polymorphism ที่สมบูรณ์