핵심 정리
cpp
class Serializable { // 순수 인터페이스
public:
virtual std::string serialize() const = 0;
virtual void deserialize(const std::string&) = 0;
virtual ~Serializable() = default;
};
class User : public Serializable {
public:
std::string serialize() const override;
void deserialize(const std::string&) override;
private:
std::string name_;
int id_;
};문법
순수 가상 함수 — 인터페이스 계약
= 0을 붙인 순수 가상 함수는 구현이 없습니다. 이를 포함한 클래스는 추상 클래스가 되어 직접 인스턴스를 만들 수 없습니다. 파생 클래스는 모든 순수 가상 함수를 구현해야 인스턴스를 만들 수 있습니다.
cpp
class Logger {
public:
virtual void log(const std::string& msg) = 0;
virtual ~Logger() = default;
};
// Logger log; // ❌ 추상 클래스 — 인스턴스 생성 불가
class ConsoleLogger : public Logger {
public:
void log(const std::string& msg) override {
std::cout << "[LOG] " << msg << "\n";
}
};
class FileLogger : public Logger {
public:
void log(const std::string& msg) override; // 파일로 기록
};
// 다형성 — 같은 인터페이스, 다른 구현
std::unique_ptr<Logger> logger = std::make_unique<ConsoleLogger>();
logger->log("started");override — 오타와 시그니처 불일치 감지
override가 없으면 시그니처가 조금이라도 다르면 새 함수로 취급되어 오버라이드가 되지 않습니다. override를 붙이면 컴파일러가 검증합니다.
cpp
class Base {
virtual int compute(int x) const { return x; }
};
class Derived : public Base {
// ❌ override 없이 시그니처 오타 — 새 함수, 오버라이드 아님
int compute(int x) { return x * 2; } // const 빠짐
// ✅ override로 컴파일 오류 감지
int compute(int x) const override { return x * 2; }
};final — 오버라이드·상속 금지
final은 더 이상 파생 클래스가 오버라이드하거나 상속할 수 없도록 합니다.
cpp
class Base {
virtual void f() { }
};
class Derived : public Base {
void f() override final { } // 이후 파생 클래스에서 f() 오버라이드 불가
};
class Leaf final : public Derived { }; // 이 클래스를 상속 불가
// class Wrong : public Leaf { }; // ❌ 컴파일 오류체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 구현을 강제하는 인터페이스 | 순수 가상 함수 = 0 |
| 오버라이드 실수 방지 | override 항상 명시 |
| 특정 가상 함수의 오버라이드 금지 | override final |
| 클래스 자체의 상속 금지 | class T final |
| 기본 구현 + 오버라이드 허용 | virtual만 (순수 아님) |
주의할 점
추상 클래스에도 가상 소멸자는 반드시 정의해야 합니다. 그렇지 않으면 기반 클래스 포인터로 delete 시 파생 소멸자가 호출되지 않습니다.
cpp
class Interface {
public:
virtual void execute() = 0;
// ❌ 소멸자 없음 — 파생 클래스 소멸자 호출 안 됨
};
// ✅ 가상 소멸자 반드시 추가
class Interface2 {
public:
virtual void execute() = 0;
virtual ~Interface2() = default;
};
class Impl : public Interface2 {
std::vector<int> data_; // 이 자원이 올바르게 해제됨
public:
void execute() override { }
};
Interface2* p = new Impl{};
delete p; // ✅ Impl::~Impl 호출 → data_ 해제참고 링크
1 sources