ความแตกต่างพื้นฐานระหว่าง C++ และ Java เกี่ยวกับ virtual functions ส่งผลต่อการออกแบบ class hierarchy และประสิทธิภาพ
ความแตกต่างหลัก
| ด้าน | C++ | Java |
|---|---|---|
| Default | Non-virtual (static binding) | Virtual (dynamic binding) |
| Keyword เพื่อ virtual | virtual | ไม่ต้องใส่ (default) |
| Keyword เพื่อ non-virtual | ไม่ใส่ virtual / final (C++11) | final |
| Performance | เร็วกว่า (ไม่มี vtable lookup) | ช้ากว่า (เสมอ vtable lookup) |
| Design Philosophy | Pay for what you use | Safety by default |
ตัวอย่าง C++: Non-virtual by Default
#include <iostream>
using namespace std;
class Base {
public:
void show() { // Non-virtual
cout << "In Base\n";
}
};
class Derived : public Base {
public:
void show() { // Hides Base::show(), ไม่ใช่ override
cout << "In Derived\n";
}
};
int main() {
Derived d;
Base& b = d; // Reference to Base
Base* bp = &d; // Pointer to Base
d.show(); // "In Derived" - static binding (compile-time)
b.show(); // "In Base" - static binding (compile-time)!
bp->show(); // "In Base" - static binding (compile-time)!
return 0;
}
ผลลัพธ์ C++
In Derived In Base In Base
ตัวอย่าง C++: ทำให้ Virtual
class Base {
public:
virtual void show() { // เพิ่ม virtual
cout << "In Base\n";
}
virtual ~Base() {} // Virtual destructor สำคัญ!
};
class Derived : public Base {
public:
void show() override { // C++11: override specifier
cout << "In Derived\n";
}
};
int main() {
Derived d;
Base& b = d;
b.show(); // "In Derived" - dynamic binding (runtime)!
}
ตัวอย่าง Java: Virtual by Default
class Base {
public void show() { // Virtual by default
System.out.println("In Base");
}
}
class Derived extends Base {
@Override
public void show() {
System.out.println("In Derived");
}
}
public class Main {
public static void main(String[] args) {
Base b = new Derived();
b.show(); // "In Derived" - dynamic binding เสมอ
}
}
ผลลัพธ์ Java
In Derived
ทำไม C++ เลือก Non-virtual by Default?
- Performance: Non-virtual เรียกโดย direct call (inline ได้) Virtual ใช้ vtable lookup (indirect call)
- Size: Class ไม่มี virtual functions ไม่มี vptr (ประหยัดหน่วยความจำ)
- Control: Programmer ตัดสินใจเองเมื่อไหร่ต้องการ polymorphism
- Backward compatibility: เข้ากันได้กับ C struct
Best Practices ใน C++
- ใช้
virtualเมื่อต้องการ polymorphic behavior - ใช้
override(C++11) เพื่อป้องกัน typo ใน signature - ใช้
final(C++11) เพื่อห้าม override ต่อ - เสมอให้ Base class มี virtual destructor ถ้ามี virtual functions
- หลีกเลี่ยงการเรียก virtual functions ใน constructor/destructor
สรุป
C++ ให้ความควบคุมและประสิทธิภาพสูงสุด (opt-in polymorphism) ส่วน Java ให้ความปลอดภัยและความง่าย (opt-out polymorphism) ทั้งสองแนวคิดมี trade-offs ที่ชัดเจน