2023. 5. 19. 03:22ㆍ카테고리 없음
추상 클래스(Abstract Class)
C++에서는 하나 이상의 순수 가상 함수를 포함하는 클래스를 추상 클래스(abstract class)라고 하며 이러한 추상 클래스는 객체 지향 프로그래밍에서 중요한 특징인 다형성을 가진 함수의 집합을 정의할 수 있게 해준다. 즉, 반드시 사용되어야 하는 멤버 함수를 추상 클래스에 순수 가상 함수로 선언해 놓으면, 이 클래스로부터 파생된 모든 클래스에서는 이 가상 함수를 반드시 재정의해야 한다. 추상 클래스는 동작이 정의되지 않은 순수 가상 함수를 포함하고 있으므로, 인스턴스를 생성할 수 없다. 따라서 추상 클래스는 먼저 상속을 통해 파생 클래스를 만들고, 만든 파생 클래스에서 순수 가상 함수를 모두 오버라이딩하고 나서야 비로소 파생 클래스의 인스턴스를 생성할 수 있게 된다. 하지만 추상 클래스 타입의 포인터와 참조는 바로 사용할 수 있다.
class Shape
{
public:
virtual void Draw() = 0;
};
int main()
{
Shape s; // 추상 클래스의 객체를 생성할 수 없기 때문에 에러가 발생한다
Shape* p;
}
- 추상 클래스는 new 연산자를 사용하여 객체를 생성할 수 없다.
- 추상 클래스(부모)와 일반 클래스(자식)는 상속의 관계에 놓여있다.
- 추상 클래스는 새로운 일반 클래스를 위한 부모 클래스의 용도로만 사용된다.
- 일반 클래스들의 필드와 메소드를 통일하여 일반 클래스 작성 시 시간을 절약할 수 있다.
- 추상 클래스는 단일 상속만 가능하며 일반 변수를 가질 수 있다.
- 추상 클래스는 동일한 부모를 가지는 클래스를 묶는 개념으로 상속을 받아서 기능을 확장시키는 것이 목적이다.
순수 가상 함수(pure virtual function)
가상 함수(virtual function)는 파생 클래스에서 재정의할 것으로 기대하는 멤버 함수를 의미하므로 가상 함수는 반드시 재정의해야만 하는 함수가 아닌, 재정의가 가능한 함수를 가리킨다. 이와는 달리 순수 가상 함수(pure virtual function)란 파생 클래스에서 반드시 재정의해야 하는 멤버 함수를 의미하며, 이러한 순수 가상 함수는 일반적으로 함수의 동작을 정의하는 본체를 가지고 있지 않는다. 따라서 파생 클래스에서 재정의하지 않으면 사용할 수 없으며, 멤버 함수의 선언에서 "= 0"구문을 통해 정의된다.
class AbstractClass
{
virtual void abstractMemberFunction() = 0; // 순수 가상 함수
virtual void abstractMemberFunction1(); // 가상함수
void abstractMemberFunction2();
};
인터페이스(Interface)
인터페이스(interface)란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미한다. 인터페이스는 추상 클래스보다 한 단계 더 추상화된 클래스라고 볼 수 있으며, 인터페이스는 구현이 없다. 즉, 인터페이스 클래스에는 가상 소멸자와 순수 가상함수만 포함된다. 인터페이스 클래스는 다형성 인터페이스, 즉 순수 가상 함수 선언을 기본 클래스로 지정하는 클래스이다. 인터페이스도 추상 클래스와 마찬가지로 new 연산자를 사용하여 객체를 생성할 수 없다. 인터페이스는 구현 객체가 같은 동작을 한다는 것을 보장하는 것이 목적이며 추상 클래스와 반대로 다중 상속이 가능하다. 다중 상속이 가능한 C++에서 잘못하면 멤버 변수와 함수가 겹칠 수 있기 때문에 다중 상속을 피하는 것이 좋은데, 인터페이스는 이 걱정 없이 다중 상속을 가능하게 해준다. 인터페이스는 멤버 변수 또는 멤버 함수가 겹칠 일이 없기 때문에 다중 상속의 단점 없이 사용할 수 있다. 단, C++은 인터페이스라는 개념을 따로 기능적으로는 지원하지 않는다. 자바에선 인터페이스라는 기능을 언어 자체에서 지원하기 때문에 interface라는 키워드도 있고 멤버 변수도 생성 불가하며 순수 가상함수만 선언이 가능하지만 C++에선 인터페이스를 따로 지원하지 않기 때문에 인터페이스를 흉내내어 인터페이스처럼 사용한다. 그래서 멤버 변수를 선언하지 못하게 강제하거나 할 수는 없다는 단점이 있다.
class Camera {
public:
void take() {
std::cout << "take picture" << std::endl;
}
};
class HDCamera {
public:
void take() {
std::cout << "take picture HD" << std::endl;
}
};
class People {
public:
void useCamera(Camera* p) { p->take(); }
void useCamera(HDCamera* p) { p->take(); }
};
int main() {
People p;
Camera c1;
p.useCamera(&c1);
HDCamera hd;
p.useCamera(&hd);
}
공통점
- 인터페이스와 추상 클래스는 객체지향의 프로그래밍 언어에서 사용되는 클래스의 종류이다.
- 둘 다 메서드의 선언만 있고 구현 내용이 없는 클래스이기 때문에 단독으로 객체를 생성할 수 없다.
- 상속받은 객체를 생성할 수 있으며 결국 자식 클래스가 무언가 반드시 구현하도록 위임해야 할 때 사용해야한다.
차이점
1. 사용 목적에서의 차이
- 추상 클래스는 공통적 기능을 하는 클래스들의 추상화이다. 이 때문에 여러 클래스들의 공통점을 찾아 추상화시켜 사용하는 것이 개발상 이득일 때 추상 클래스를 구현하여 사용한다.
- 인터페이스는 구현하고자 하는 클래스들에 대해 특정 메서드가 반드시 존재하도록 강제한다. 따라서 인터페이스를 통해 구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용한다.
2. 형태 및 다중 상속 여부
추상 클래스는 미구현(선언만 되어있는) 멤버 메서드를 포함할 수도 있고 인터페이스는 메서드의 선언만(뼈대만) 존재하며 추상 클래스는 하나 이상의 순수 가상 함수를 포함하기만 한다면 다른 함수도 포함 가능하지만 인터페이스의 경우 순수 가상 함수로만을 포함한다. 추상 클래스는 단일 상속이며 인터페이스는 다중 상속이다.