기본 패턴
cpp
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};설명
- 순수 가상 함수는 파생 클래스가 반드시 구현해야 하는 인터페이스를 정의하며, 이런 함수를 가진 클래스는 추상 클래스가 됩니다.
- 추상 클래스는 직접 객체를 만드는 용도보다, "이 계열의 타입은 최소한 이런 동작을 제공해야 한다"는 계약을 표현하는 데 가깝습니다.
- 파생 클래스에서 함수를 재정의할 때
override를 붙이면 시그니처가 어긋났을 때 컴파일러가 바로 잡아 주므로, 다형성 버그를 크게 줄일 수 있습니다. - 기반 클래스를 포인터나 참조로 다룰 때는 가상 소멸자를 두는 것이 중요합니다. 그렇지 않으면 파생 클래스 자원이 제대로 정리되지 않을 수 있습니다.
- 객체지향 설계에서 상속은 코드 재사용 도구이면서 동시에 인터페이스 계약 수단이므로, "공통 기반 추상화가 정말 있는가"를 먼저 보는 편이 좋습니다.
짧은 예제
cpp
#include <iostream>
#include <memory>
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Square : public Shape {
public:
explicit Square(double side) : side(side) {}
double area() const override { return side * side; }
private:
double side;
};
int main() {
std::unique_ptr<Shape> shape = std::make_unique<Square>(3.0);
std::cout << shape->area() << "\n";
}빠른 정리
| 항목 | 설명 |
|---|---|
virtual ... = 0 | 순수 가상 함수 |
| 추상 클래스 | 직접 인스턴스화할 수 없는 기반 클래스 |
override | 기반 클래스 가상 함수를 정확히 재정의함을 명시 |
| 가상 소멸자 | 기반 포인터로 지울 때 안전한 정리를 보장 |
| 용도 | 공통 인터페이스와 다형성 표현 |
주의할 점
기반 클래스에 가상 소멸자가 없는데 파생 객체를 기반 포인터로 삭제하면 자원 정리가 불완전해질 수 있습니다. 다형성 기반 클래스라면 소멸자 설계를 먼저 확인해야 합니다.