C++에서는 클래스의 멤버 함수를 NULL 객체 포인터를 통해서도 호출할 수 있습니다. 언뜻 이상하게 들리지만, 실제로 컴파일하여 실행하면 정상적으로 동작하는 경우가 있습니다.
주의: 이러한 동작은 C++ 표준에서 정의되지 않은 동작(undefined behavior)에 해당합니다. 따라서 프로그램의 실행 결과에 대한 어떠한 보장도 없으며, 실제 결과는 사용하는 컴파일러와 실행 환경에 따라 달라질 수 있습니다.
다음 예제 프로그램을 통해 이를 직접 확인해 보겠습니다.
예제 코드
#include <iostream>
using namespace std;
class Demo {
public :
void fun() {
cout << "This member function is called through Null object pointer.";
}
};
int main() {
Demo *ptr = NULL;
ptr->fun();
return 0;
}출력 결과
위 프로그램을 실행하면 다음과 같은 결과가 출력됩니다.
This member function is called through Null object pointer.
이제 위 프로그램이 어떻게 동작하는지 단계별로 살펴보겠습니다.
클래스 Demo에는 멤버 함수 fun()이 정의되어 있으며, 이 함수는 "This member function is called through Null object pointer."라는 문장을 화면에 출력합니다. 해당 코드는 다음과 같습니다.
class Demo {
public :
void fun() {
cout << "This member function is called through Null object pointer.";
}
};main() 함수에서는 Demo 타입의 포인터 변수 ptr을 선언하고 NULL로 초기화한 뒤, 이 포인터를 사용해 멤버 함수 fun()을 호출합니다. 해당 코드는 다음과 같습니다.
int main() {
Demo *ptr = NULL;
ptr->fun();
return 0;
}NULL 포인터로 호출이 가능한 이유
멤버 함수는 내부적으로 this 포인터를 암묵적인 매개변수로 전달받습니다. 그런데 fun()처럼 객체의 멤버 변수를 참조하지 않는 함수라면, this 포인터가 NULL이어도 함수 본문을 실행하는 데 아무런 문제가 없습니다. 컴파일러는 ptr->fun()을 사실상 일반 함수 호출처럼 처리하기 때문에 NULL 포인터 역참조 오류가 발생하지 않는 것입니다.
반면, 멤버 변수에 접근하거나 가상 함수(virtual function)를 호출하는 경우에는 this 포인터가 실제로 사용되므로, NULL 객체 포인터로 호출하면 세그먼테이션 폴트(segmentation fault) 같은 런타임 오류가 발생합니다. 따라서 이러한 방식은 학습 목적 외에는 절대 실무 코드에서 사용해서는 안 됩니다.