기본 패턴
cpp
class Animal {
public:
virtual ~Animal() = default;
virtual void speak() const = 0;
};
class Dog : public Animal {
public:
void speak() const override { std::cout << "woof\n"; }
};설명
- C++에서 다형성을 제대로 쓰려면 "기반 클래스 포인터나 참조로 파생 객체를 다룬다"는 전제가 깔립니다. 이때 소멸자까지 다형적으로 동작해야 자원 정리가 안전합니다.
- 그래서 기반 클래스를 다형적으로 사용할 계획이라면 보통
virtual ~Base() = default;를 함께 둡니다. 그래야Base*로delete해도 실제 파생 클래스 소멸자가 호출됩니다. - object slicing은 파생 객체를 기반 클래스 "값"으로 복사할 때 파생 부분이 잘려 나가는 현상입니다. 즉 다형성을 기대했는데 값 복사 때문에 기반 부분만 남아 버리는 문제입니다.
- 이 주제의 핵심은 "다형성은 값이 아니라 참조/포인터로 유지된다"는 점입니다. 값 복사를 하면 타입 계층의 정보가 줄어들 수 있고, 그 순간 virtual dispatch도 기대와 다르게 보일 수 있습니다.
- 대학 교재 수준에서는 상속 문법보다 이 메모리 모델을 이해하는 것이 더 중요합니다. 그래야 왜 컨테이너에
Base값을 넣는 것과std::unique_ptr<Base>를 넣는 것이 다르게 동작하는지도 자연스럽게 연결됩니다.
빠른 정리
| 개념 | 의미 |
|---|---|
| virtual destructor | 기반 클래스 포인터 삭제 시 파생 소멸자까지 호출 |
| object slicing | 파생 객체를 기반 클래스 값으로 복사하며 정보 손실 |
| 안전한 다형성 | 기반 클래스 참조/포인터 사용 |
| 흔한 실수 | Base 값을 복사해 다형성을 기대함 |
주의할 점
다형적으로 쓸 클래스를 값으로 전달하거나 컨테이너에 값으로 담으면 object slicing이 발생할 수 있습니다. 기반 타입으로 다형성을 유지하고 싶다면 참조, 포인터, 스마트 포인터를 먼저 떠올리는 편이 좋습니다.
참고 링크
2 sources