ภาพรวม
คล้ายกับบทความก่อนหน้า การเรียก Member Function ผ่าน NULL Class Pointer ใน C++ เป็น Undefined Behavior (UB) แม้จะ "ทำงานได้" บนบาง Compiler แต่เป็นการเขียนโค้ดที่ผิดและอันตราย
ตัวอย่างโค้ด (แสดงพฤติกรรม - ไม่แนะนำ)
#include <iostream>
using namespace std;
class Example {
public:
void func() {
cout << "The function is called through Null class pointer.";
}
};
int main() {
Example* p = nullptr;
p->func(); // Undefined Behavior!
return 0;
}
ผลลัพธ์ (บนบาง Compiler)
The function is called through Null class pointer.
ทำไมถึงเป็น Undefined Behavior?
- C++ Standard ([expr.call]): "If the object expression is a null pointer, the behavior is undefined"
- Non-virtual Functions: Compile เป็นฟังก์ชันทั่วไปรับ
thispointer → ถ้าไม่ใช้thisอาจรันได้ - Virtual Functions: ต้องอ่าน VTable ผ่าน
this→ Crash ทันที - Data Member Access: Dereference Null Pointer → Crash
ตัวอย่างที่ Crash แน่นอน
class WillCrash {
int data = 42;
public:
void accessData() { cout << data; } // Crash: this->data
virtual void virtualMethod() {} // Crash: vtable lookup
};
int main() {
WillCrash* p = nullptr;
p->accessData(); // Crash
p->virtualMethod(); // Crash
}
การป้องกันและ Best Practices
1. ตรวจสอบ Null Pointer เสมอ
if (ptr != nullptr) {
ptr->func();
}
2. ใช้ References แทน Pointers (เมื่อไม่ต้องการ Null)
void safeCall(Example& obj) { // Reference ไม่เป็น Null ได้
obj.func();
}
3. ใช้ Smart Pointers
auto ptr = make_unique();
if (ptr) ptr->func(); // Safe
// หรือ
ptr->func(); // Safe ถ้า ensure ไม่เป็น null
4. Contract/Assertions (Debug Build)
#include
assert(ptr != nullptr);
ptr->func();
5. C++20 Contracts (Proposed)
[[pre: ptr != nullptr]] ptr->func();
Static Analysis Tools
- Clang-Tidy:
bugprone-null-dereference - Cppcheck: Null Pointer Dereference
- PVS-Studio: V522, V595, V757
- Compiler Warnings:
-Wnull-dereference(GCC/Clang)
สรุป
| สถานการณ์ | พฤติกรรม | มาตรฐาน |
|---|---|---|
| Non-virtual, No Data Access | อาจ "ทำงานได้" | UB |
| Access Data Member | Crash (Segfault) | UB |
| Virtual Function Call | Crash (Segfault) | UB |
| Dynamic Cast / typeid | Crash/UB | UB |
ข้อสรุป: ห้ามเรียก Member Function ผ่าน Null Pointer เด็ดขาด ใช้ References, Smart Pointers, หรือ Null Checks แทน