Fundamental of CS/: : C++

[Inheritance] Pure Virtual Function

Jay.P Morgan 2024. 7. 16. 11:04

 

 

Pure Virtual Function, Abstract Class, Interface Class 정리

이번 시간에는 C++의 Pure Virtual Function(순수 가상 함수), Abstract Class(추상 클래스), 그리고 이를 활용한 Interface Class(인터페이스 클래스)에 대해 알아보았습니다.


1. Pure Virtual Function(순수 가상 함수)

개념

일반적인 Virtual Function은 Base 클래스에서 기본 구현(Implementation)을 제공할 수 있습니다.

예:

virtual void Speak()
{
    cout << "Animal";
}

하지만 Pure Virtual Function은 함수의 구현 부분이 없는 Virtual Function입니다.

C++에서는 함수 선언 뒤에 = 0을 붙여서 만듭니다.

virtual void Speak() = 0;

즉:

  • 함수 선언만 존재
  • 구현은 Derived Class가 담당

하는 구조입니다.


2. Abstract Class(추상 클래스)

Pure Virtual Function을 하나라도 가지고 있는 클래스를:

Abstract Class(추상 클래스)

라고 합니다.

예:

class Animal
{
public:
    virtual void Speak() = 0;
};

Animal 클래스는 Pure Virtual Function인 Speak()을 가지고 있기 때문에 Abstract Class가 됩니다.


Abstract Class의 특징

1) 객체 생성 불가능

추상 클래스는 직접 객체를 만들 수 없습니다.

Animal animal;

같은 코드는 컴파일 에러가 발생합니다.

이유:

  • 추상 클래스는 완성된 객체가 아님
  • Pure Virtual Function의 구현이 없기 때문에 호출할 수 없음

입니다.


2) Derived Class에서 반드시 구현 필요

상속받은 클래스는 Pure Virtual Function을 반드시 Override 해야 합니다.

예:

class Cat : public Animal
{
public:
    void Speak() override
    {
        cout << "Meow";
    }
};

이렇게 구현하면 Cat 객체 생성이 가능합니다.

반대로:

class Cat : public Animal
{
};

처럼 구현하지 않으면 Cat 역시 Abstract Class가 됩니다.


3. Abstract Class를 활용한 Interface Class

Pure Virtual Function을 이용하면 Interface Class를 만들 수 있습니다.

Interface Class의 목적은:

클래스가 반드시 제공해야 하는 기능의 규칙을 정의하는 것

입니다.

예를 들어 게임에서:

class Monster
{
public:
    virtual void Attack() = 0;
    virtual void Defense() = 0;
};

라고 정의할 수 있습니다.


그러면:

class Goblin : public Monster
class RabbitMonster : public Monster

같은 클래스들은 반드시:

  • Attack()
  • Defense()

를 구현해야 합니다.


Interface Class의 장점

게임 전체 구조를 설계하는 개발자는:

Monster

라는 큰 인터페이스만 정의합니다.

각 몬스터 개발자는:

Goblin
Rabbit
Dragon

등의 세부 구현만 담당합니다.

즉:

  • 설계 담당자 → 인터페이스 정의
  • 구현 담당자 → 세부 기능 구현

으로 역할 분리가 가능합니다.


4. 좋은 Interface Class 설계 방법

유연한 인터페이스를 만들기 위해서는:

1) 멤버 변수 제거

좋은 Interface Class는 보통:

  • 멤버 변수 없음
  • Pure Virtual Function만 존재

하는 형태가 좋습니다.

예:

class Animal
{
public:
    virtual void Speak() = 0;
    virtual void Move() = 0;
};

2) 구현 코드 넣지 않기

인터페이스는 "무엇을 해야 하는가"만 정의해야 합니다.

"어떻게 하는가"는 Derived Class가 담당합니다.


5. 잘못된 Interface 설계 예제

처음에는 다음처럼 만들 수 있습니다.

class Animal
{
public:
    int LegCount;
    string NoseLocation;

    void Walk();
};

고양이, 강아지 같은 동물에는 문제가 없습니다.

하지만 새로운 동물이 추가되면 문제가 생깁니다.


문제: Dolphin 추가

돌고래는:

  • 다리가 없음
  • 걷지 않음
  • 코 위치 개념이 다름

하지만 Animal 클래스를 상속받으면:

int LegCount;
string NoseLocation;

같은 필요 없는 데이터까지 가지게 됩니다.


또한:

Walk()

도 문제가 됩니다.

돌고래는 걸을 수 없기 때문에:

override Walk()

를 억지로 구현해야 합니다.

결과적으로:

  • 필요 없는 멤버 변수 증가
  • 의미 없는 함수 구현 증가
  • 유지보수 어려움

이 발생합니다.


6. 더 좋은 구조

Animal을 인터페이스 형태로 변경합니다.

class Animal
{
public:
    virtual void Speak() = 0;
    virtual void Move() = 0;
};

그러면:

Cat:

class Cat : public Animal
{
    int legCount;

    void Move() override
    {
        // 걷기
    }
};

Dolphin:

class Dolphin : public Animal
{
    int finCount;

    void Move() override
    {
        // 수영
    }
};

처럼 각각 필요한 데이터만 자유롭게 추가할 수 있습니다.


7. Interface Class의 단점

Interface 방식은 유연하지만 단점도 있습니다.

모든 Derived Class가:

virtual function

을 직접 구현해야 합니다.

즉 코드 중복이 발생할 수 있습니다.

예:

모든 동물이 공통적으로 가지고 있는 기능:

  • 숨쉬기
  • 에너지 관리
  • 체력 계산

등을 매번 구현해야 합니다.


8. 해결 방법: Interface + Implementation 분리

이 문제는 다중 상속을 이용해 해결할 수 있습니다.

구조:

          Animal Interface
                |
        -----------------
        |               |
   Animal Base      Cat
   Implementation

또는:

Cat
 |
 +---- Animal Interface
 |
 +---- Animal Implementation

형태로 구성합니다.


이렇게 하면:

Interface 장점

  • 일관된 구조
  • 유지보수성 향상
  • 확장성 증가

Implementation 장점

  • 코드 재사용
  • 중복 제거

두 가지 장점을 모두 얻을 수 있습니다.

단, 이 과정에서는:

Multiple Inheritance(다중 상속)

개념이 필요합니다.


핵심 정리

개념설명

Pure Virtual Function 구현이 없는 Virtual Function (=0)
Abstract Class Pure Virtual Function을 하나 이상 가진 클래스
Abstract Class 객체 생성 불가능
Derived Class Pure Virtual Function 반드시 Override 필요
Interface Class Pure Virtual Function만 이용한 설계 방식
Interface 설계 원칙 멤버 변수와 구현 코드 최소화
장점 유지보수성, 확장성 향상
단점 코드 중복 발생 가능
해결 방법 Interface + Implementation 분리, 다중 상속 활용

결론적으로 C++에서 Pure Virtual Function과 Abstract Class는 좋은 소프트웨어 구조를 만들기 위한 중요한 도구입니다.

특히 Interface Class는 큰 프로젝트에서 클래스 간 의존성을 줄이고 확장 가능한 구조를 만드는 데 사용됩니다.

다만 인터페이스만 사용하면 코드 재사용성이 떨어질 수 있기 때문에, 필요에 따라 Implementation Class와 다중 상속을 조합하는 방식도 활용할 수 있습니다.

 

 

 

 

'Fundamental of CS > : : C++' 카테고리의 다른 글

[Inheritance] Object Slicing  (0) 2024.07.16
[Inheritance] Multiple Inheritance  (0) 2024.07.16
[Inheritance] Virtual Table  (0) 2024.07.16
[Inheritance] Virtual Function  (0) 2024.07.16
[Inheritance] C++ 상속(Inheritance) Intro  (0) 2024.07.16