추상 함수와 추상 클래스(Abstract Class) 그리고 인터페이스(Interface)
순수 가상 함수(Pure Virtual Function)
순수 가상 함수는 구현이 없는 가상 함수로, 파생 클래스에서 반드시 재정의해야 하는 함수를 의미합니다.
순수 가상 함수는 클래스의 선언에서 = 0
으로 표시됩니다.
1
2
3
4
5
class Base
{
public:
virtual void pureVirtualFunction() = 0; // 순수 가상 함수
};
이 함수는 구현이 없으며, 이 클래스를 상속받은 파생 클래스에서 이 함수를 반드시 구현해야 합니다.
추상 클래스(Abstract Class)
추상 클래스는 한 개 이상의 순수 가상 함수를 포함하는 클래스입니다.
추상 클래스는 그 자체로는 인스턴스화할 수 없으며, 이 클래스를 상속받는 파생 클래스에서 순수 가상 함수를 구현해야 합니다.
단, 추상 클래스 타입의 포인터나 참조로는 사용할 수 있습니다.
(당연하게도 추상 클래스를 사용하는 이유 중 하나가, 공통적인 동작에 대한 다형적인 접근을 사용할 수 있게 하기 위해서도 있기 때문입니다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Derived : public Base
{
public:
void pureVirtualFunction() override
{
// 순수 가상 함수 구현
}
};
int main()
{
Base* ptr = new Derived();
ptr->pureVirtualFunction();
}
인터페이스(Interface)
C++에서는 인터페이스라는 개념이 명시적으로 존재하지 않지만, 순수 가상 함수만을 가지는 추상 클래스를 인터페이스라고 부릅니다.
실제로 추상 클래스(Abstract Class)는 순수 가상 함수(Pure Virtual Function)만을 가지는게 아니라, 구현 할 수 있는 가상 함수라던가, 멤버 변수 또한 가질 수 있습니다.
즉, 추상 클래스란, 공통적인 기능을 여러 클래스에 제공하면서도, 일부 메서드는 하위 클래스에서 반드시 구현하도록 강제할 때 유용합니다.
하지만, 인터페이스(Interface)는 다형성을 구현하고, 클래스 간의 결합도를 낮추는데 목적이 있습니다.
조금 다르게 말하자면, 인터페이스는 서로 다른 클래스나 컴포넌트가 클래스의 내부 구현을 외부에 노출하지 않고 공통된 방법으로 상호 작용할 수 있도록 합니다.
이는 정보 은닉과 캡슐화를 강화하여 유지보수성과 확장성을 높이고 다형성(polymorphism)을 가능하게 하여 코드의 유연성을 높입니다.
보다 쉽게 설명드리자면,
- 서로 다른 클래스나 컴포넌트가
- 공통된 방법(인터페이스에서 정의한 메서드)으로 상호작용 하게 하고 싶은데
- 어딘가에 특정(상속)되지도 않고, 어쩌면 떨어져 있어도 될 것 같은 기능이라면 인터페이스로 구현
예를 들어, 게임 내에서 플레이어, 몬스터, 건물 등 다양한 객체가 있을 수 있고, 이들 모두 다양한 방법으로 데미지를 받을 수 있습니다.
하지만, 데미지를 처리하는 기능을 상속으로 구현하기에는 “어딘가에 특정(상속)되지도 않고”에 해당하는 것 같습니다.
이때, 이러한 객체들이 데미지를 받는 행동을 공통된 인터페이스를 통해 처리할 수 있습니다.
1.데미지 인터페이스 정의
1
2
3
4
5
class IDamageable
{
public:
virtual void takeDamage(int amount) = 0;
};
2.각 클래스에서 IDamageable 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Player : public IDamageable {
private:
int health;
public:
Player(int h) : health(h) {}
void takeDamage(int amount) override {
health -= amount;
cout << "Player가 " << amount << "의 데미지를 받았습니다. 남은 체력: " << health << endl;
}
};
class Monster : public IDamageable {
private:
int health;
public:
Monster(int h) : health(h) {}
void takeDamage(int amount) override {
health -= amount;
cout << "Monster가 " << amount << "의 데미지를 받았습니다. 남은 체력: " << health << endl;
}
};
class Building : public IDamageable {
private:
int durability;
public:
Building(int d) : durability(d) {}
void takeDamage(int amount) override {
durability -= amount;
cout << "Building이 " << amount << "의 충격을 받았습니다. 남은 내구도: " << durability << endl;
}
};