g) 순수 가상함수랑 그냥 가상함수랑 차이

 

 

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 역시 추상 클래스로 간주되고 객체를 생성할 수 없습니다.

 

 

이 차이는 객체 지향 설계에서 중요한 의미를 가지며, 클래스 계층에서 특정 메소드의 구현을 강제하거나 선택적으로 제공할 수 있는 유연성을 제공합니다.