C++객체지향

추상 클래스와 override

순수 가상 함수, 추상 클래스, `override` 키워드가 다형성 코드를 어떻게 안전하게 만드는지 정리합니다.

마지막 수정 2026년 3월 19일

기본 패턴

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기반 클래스 가상 함수를 정확히 재정의함을 명시
가상 소멸자기반 포인터로 지울 때 안전한 정리를 보장
용도공통 인터페이스와 다형성 표현

주의할 점

기반 클래스에 가상 소멸자가 없는데 파생 객체를 기반 포인터로 삭제하면 자원 정리가 불완전해질 수 있습니다. 다형성 기반 클래스라면 소멸자 설계를 먼저 확인해야 합니다.