virtual 함수에 = 0을 붙이는 것과 붙이지 않는 것 사이에는 중요한 차이가 있습니다. 이 두 가지 방식은 클래스의 설계와 객체 지향 프로그래밍에서 다형성을 활용하는 방식에 영향을 미칩니다.
=0을 붙인 경우: 순수 가상 함수 (Pure Virtual Function)
- 정의: 함수 선언 끝에 = 0을 붙여 해당 함수를 순수 가상 함수로 선언합니다. 이는 함수에 대한 구현을 제공하지 않으며, 파생 클래스에서 반드시 구현해야 함을 의미합니다.
- 추상 클래스: 순수 가상 함수를 하나라도 포함하는 클래스는 추상 클래스가 됩니다. 추상 클래스의 인스턴스는 생성할 수 없으며, 오직 파생 클래스를 통해서만 사용될 수 있습니다.
- 용도: 인터페이스를 정의하는 데 주로 사용됩니다. 이를 통해 파생 클래스에서 특정 메소드를 구현하도록 강제할 수 있습니다.
=0을 붙이지 않은 경우: 가상 함수 (Virtual Function)
- 정의: 함수 선언에서 virtual 키워드만 사용하고, 함수 본문을 제공하는 방식입니다. 이는 해당 함수가 파생 클래스에 의해 오버라이드될 수 있음을 의미하지만, 꼭 그래야 하는 것은 아닙니다.
- 가상 함수 테이블: 가상 함수는 가상 함수 테이블(vtable)에 등록되어, 런타임에 동적 바인딩을 통해 해당 함수의 적절한 버전이 호출될 수 있도록 합니다.
- 용도: 가상 함수는 기본적인 행동을 정의하고, 파생 클래스에서 이를 변경하거나 확장할 수 있는 유연성을 제공합니다. 파생 클래스에서 이 함수를 오버라이드할 수도 있고, 그렇지 않을 수도 있습니다.
예시
다음은 이 두 가지 방식의 차이를 보여주는 예시입니다:
class Base {
public:
virtual void func() { std::cout << "Base func" << std::endl; } // 가상 함수
virtual void pureFunc() = 0; // 순수 가상 함수
};
class Derived : public Base {
public:
void func() override { std::cout << "Derived func" << std::endl; } // 가상 함수 오버라이드
void pureFunc() override { std::cout << "Derived pureFunc" << std::endl; } // 순수 가상 함수 구현
};
int main() {
// Base b; // 에러: 추상 클래스의 인스턴스를 생성할 수 없습니다.
Derived d;
Base* ptr = &d;
ptr->func(); // "Derived func"
ptr->pureFunc(); // "Derived pureFunc"
return 0;
}
이 예시에서 Base 클래스는 pureFunc이 순수 가상 함수이므로 추상 클래스입니다. 따라서 Base의 객체를 직접 생성할 수 없습니다. Derived 클래스는 이 순수 가상 함수를 구현하며, 가상 함수 func도 오버라이드합니다.
결론
순수 가상 함수와 가상 함수 사이의 선택은 설계 의도에 따라 달라집니다. 순수 가상 함수는 클래스의 인터페이스를 엄격하게 정의하고 싶을 때 사용하며, 가상 함수는 더 유연한 구현과 오버라이드 가능성을 제공합니다.
class Base {
public:
virtual void func() { std::cout << "Base func" << std::endl; } // 가상 함수
virtual void pureFunc() = 0; // 순수 가상 함수
};
class Derived : public Base {
public:
void func() override { std::cout << "Derived func" << std::endl; } // 가상 함수 오버라이드
// 순수 가상 함수 pureFunc() 구현이 생략됨: 이 경우 컴파일 오류 발생
};
int main() {
// Base b; // 에러: 추상 클래스의 인스턴스를 생성할 수 없습니다.
// Derived d; // 에러: pureFunc()의 구현이 없기 때문에 Derived도 추상 클래스로 간주됩니다.
Base* ptr; // Base 타입의 포인터 선언은 가능
// ptr = &d; // 에러: Derived는 추상 클래스이므로 객체를 생성할 수 없습니다.
return 0;
}
주요 차이점 설명
- 가상 함수 (func): Base 클래스에서 기본 구현을 제공하고 있으며, Derived 클래스에서 이를 오버라이드할 수 있습니다. func이 제공되어 있기 때문에, 이를 오버라이드하지 않아도 Derived 클래스의 객체를 생성할 수 있습니다.
- 순수 가상 함수 (pureFunc): Base 클래스에서 이 함수의 구현을 제공하지 않습니다 (= 0). 이것이 Base를 추상 클래스로 만듭니다. 모든 파생 클래스는 pureFunc을 구현해야 하며, 그렇지 않으면 해당 파생 클래스도 추상 클래스로 간주되어 객체를 생성할 수 없습니다. 이 예제에서 Derived 클래스에서 pureFunc을 구현하지 않았기 때문에, Derived 역시 추상 클래스로 간주되고 객체를 생성할 수 없습니다.
이 차이는 객체 지향 설계에서 중요한 의미를 가지며, 클래스 계층에서 특정 메소드의 구현을 강제하거나 선택적으로 제공할 수 있는 유연성을 제공합니다.
'그때 그때 메모' 카테고리의 다른 글
MIPS 어셈블리어로 팩토리얼 함수 계산 과정 (0) | 2024.10.19 |
---|---|
g) Action Delegate, Invoke() 메서드 (0) | 2024.07.26 |
g) 왜 클래스 이름으로 type을 만들까, static과 매니저 (0) | 2024.07.25 |
리플렉션 (요약) (0) | 2024.07.25 |
g) nullable (0) | 2024.07.25 |