[2026] C++ override와 final | 가상 함수 가이드

[2026] C++ override와 final | 가상 함수 가이드

이 글의 핵심

가상 함수 오버라이드, override의 역할, final 클래스·함수, devirtualization과 성능까지 정리한 가이드입니다.

override란?

override 는 C++11에서 도입된 키워드로, 가상 함수를 오버라이드한다는 것을 명시적으로 표시합니다. 컴파일러가 실제로 기반 클래스의 가상 함수를 오버라이드하는지 검증하여, 오타나 시그니처 불일치로 인한 버그를 방지합니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func() {
        std::cout << "Base::func" << std::endl;
    }
};
class Derived : public Base {
public:
    // override 명시
    void func() override {
        std::cout << "Derived::func" << std::endl;
    }
};

왜 필요한가?:

  • 오타 방지: 함수 이름 오타 시 컴파일 에러
  • 시그니처 검증: 매개변수나 const 불일치 감지
  • 명확한 의도: 코드 리더에게 오버라이드 의도 전달
  • 리팩토링 안전: 기반 클래스 변경 시 파생 클래스 오류 감지 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Base {
public:
    virtual void process(int x) {}
};
class Derived : public Base {
public:
    // ❌ override 없음: 오타 발견 못함
    void proccess(int x) {}  // 새 함수 생성 (버그!)
    
    // ✅ override 사용: 컴파일 에러
    void proccess(int x) override {}  // 에러: 오버라이드할 함수 없음
};

override의 동작 원리: override는 컴파일러에게 “이 함수는 기반 클래스의 가상 함수를 오버라이드한다”고 알립니다. 컴파일러는 다음을 검증합니다:

  1. 기반 클래스에 같은 이름의 가상 함수가 있는지
  2. 시그니처(매개변수, const, 반환 타입)가 일치하는지
  3. 기반 클래스 함수가 final이 아닌지 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Base {
public:
    virtual void func(int x) const {}
};
class Derived : public Base {
public:
    // 모두 컴파일 에러
    // void func(int x) override {}           // 에러: const 누락
    // void func(double x) const override {}  // 에러: 매개변수 타입 다름
    // int func(int x) const override {}      // 에러: 반환 타입 다름 (공변 반환 제외)
    
    // ✅ 올바른 오버라이드
    void func(int x) const override {}
};

가상 함수 오버라이드란 무엇인가

기반 클래스에 virtual이 붙은 멤버 함수는 동적 디스패치 대상입니다. 파생 클래스에서 같은 시그니처로 선언하면, 기반 포인터/참조를 통해 호출할 때 파생 구현이 실행됩니다. 이것이 오버라이드입니다.

  • vtable: 대부분의 구현에서 가상 함수는 함수 포인터 테이블을 통해 간접 호출됩니다.
  • 시그니처 일치: 이름·매개변수·const·참조 한정자·일부 반환 타입(공변 반환)까지 맞아야 같은 슬롯을 덮어씁니다. 하나라도 다르면 새 함수가 될 수 있어 버그가 납니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
struct Base {
    virtual void f(int) const {}
    virtual ~Base() = default;
};
struct Derived : Base {
    void f(int) const override {}  // Base::f를 오버라이드
};

override 키워드가 중요한 이유

override컴파일러에게 검증을 맡기는 표식입니다. 없으면 다음 실수가 조용히 통과합니다.

  1. 이름 오타: draw 대신 drwa — 새 함수가 생김.
  2. const 누락: 기반은 void f() const, 파생은 void f() — 오버라이드 아님.
  3. 매개변수 타입 변경: intsize_t 등 — 또 다른 새 함수.
  4. 기반 쪽 변경: 기반에서 virtual이 빠지거나 시그니처가 바뀌면, 파생은 전혀 다른 함수를 들고 있을 수 있음. override를 붙이면 위 경우 즉시 컴파일 에러가 나므로, 다형성 계약이 깨지는 것을 빌드 단계에서 막습니다.

override의 장점

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func(int x) {}
};
class Derived : public Base {
public:
    // ❌ 오타 (새 함수 생성)
    void fucn(int x) {}  // 오타!
    
    // ✅ override (컴파일 에러)
    void fucn(int x) override {}  // 에러: 오버라이드할 함수 없음
};

final이란?

final 은 C++11에서 도입된 키워드로, 상속이나 오버라이드를 금지합니다. 클래스에 사용하면 더 이상 상속할 수 없고, 가상 함수에 사용하면 더 이상 오버라이드할 수 없습니다. 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 클래스 final: 상속 금지
class FinalClass final {
public:
    void func() {}
};
// class Derived : public FinalClass {};  // 에러
// 함수 final: 오버라이드 금지
class Base {
public:
    virtual void func() final {}
};
class Derived : public Base {
public:
    // void func() override {}  // 에러: final 함수
};

왜 필요한가?:

  • 설계 의도 명시: 더 이상 확장하지 않을 클래스/함수 표시
  • 보안: 중요한 로직이 변경되지 않도록 보호
  • 성능 최적화: 컴파일러가 devirtualization 수행 가능
  • 안정성: 예상치 못한 상속으로 인한 버그 방지 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ final 없음: 의도하지 않은 상속 가능
class SecurityManager {
public:
    virtual void authenticate() {
        // 중요한 보안 로직
    }
};
class HackedManager : public SecurityManager {
public:
    void authenticate() override {
        // 보안 우회!
    }
};
// ✅ final 사용: 상속 금지
class SecurityManager final {
public:
    void authenticate() {
        // 중요한 보안 로직 (안전)
    }
};
// class HackedManager : public SecurityManager {};  // 에러

final의 성능 이점: final을 사용하면 컴파일러가 devirtualization을 수행할 수 있습니다. 가상 함수 호출을 직접 호출로 변환하여 vtable 조회 오버헤드를 제거합니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func() final {
        // 더 이상 오버라이드 없음
        // 컴파일러가 직접 호출로 최적화 가능
    }
};
// 호출 시
Base* ptr = new Base();
ptr->func();  // 직접 호출로 최적화 가능 (vtable 조회 불필요)

final 클래스 vs final 함수:

특징final 클래스final 함수
적용 대상클래스 전체특정 가상 함수
효과상속 금지오버라이드 금지
사용 위치클래스 선언가상 함수 선언
성능모든 가상 함수 최적화해당 함수만 최적화
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// final 클래스
class FinalClass final {
    virtual void func1() {}
    virtual void func2() {}
};
// final 함수
class Base {
    virtual void func1() final {}  // 이 함수만 final
    virtual void func2() {}        // 오버라이드 가능
};

final 클래스 vs final 함수 — 선택 가이드

구분의미쓰는 때
class D finalD를 상속할 수 없음타입 계층을 여기서 끝낸다고 확정할 때(보안·성능·불변식).
virtual void f() finalf는 파생에서 더 이상 오버라이드 불가파이프라인의 한 단계만 고정하고 나머지 훅은 열어둘 때.
final 클래스는 해당 클래스의 모든 가상 호출에 대해 “이 아래로는 더 이상 파생이 없다”는 정보를 컴파일러에 줄 수 있어 최적화(아래 ‘성능’ 참고)에 유리한 경우가 있습니다. final 함수한 메서드만 닫을 때 씁니다. 확장 가능성을 나중에 열어야 할지 불확실하면 남용하지 않는 것이 좋습니다.

실전 예시

예시 1: 도형 클래스

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Shape {
public:
    virtual double area() const = 0;
    virtual void draw() const = 0;
    virtual ~Shape() = default;
};
class Circle : public Shape {
private:
    double radius;
    
public:
    Circle(double r) : radius(r) {}
    
    double area() const override {
        return 3.14159 * radius * radius;
    }
    
    void draw() const override {
        std::cout << "Drawing Circle" << std::endl;
    }
};
class Rectangle final : public Shape {
private:
    double width, height;
    
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double area() const override final {
        return width * height;
    }
    
    void draw() const override {
        std::cout << "Drawing Rectangle" << std::endl;
    }
};
// class Square : public Rectangle {};  // 에러: final 클래스

예시 2: 로거 계층

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Logger {
public:
    virtual void log(const std::string& message) {
        std::cout << "[LOG] " << message << std::endl;
    }
    
    virtual ~Logger() = default;
};
class FileLogger : public Logger {
private:
    std::ofstream file;
    
public:
    FileLogger(const std::string& filename) : file(filename) {}
    
    void log(const std::string& message) override {
        file << "[FILE] " << message << std::endl;
    }
};
class SecureLogger final : public FileLogger {
public:
    SecureLogger(const std::string& filename) 
        : FileLogger(filename) {}
    
    void log(const std::string& message) override final {
        // 암호화 후 로깅
        FileLogger::log("ENCRYPTED: " + message);
    }
};

예시 3: 템플릿 메서드 패턴

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class GameCharacter {
public:
    void takeDamage(int damage) {
        beforeDamage();
        applyDamage(damage);
        afterDamage();
    }
    
    virtual ~GameCharacter() = default;
    
protected:
    virtual void beforeDamage() {}
    virtual void applyDamage(int damage) = 0;
    virtual void afterDamage() {}
};
class Warrior : public GameCharacter {
private:
    int health = 100;
    int armor = 50;
    
protected:
    void beforeDamage() override {
        std::cout << "방어 자세" << std::endl;
    }
    
    void applyDamage(int damage) override final {
        int actualDamage = std::max(0, damage - armor);
        health -= actualDamage;
        std::cout << "데미지: " << actualDamage << std::endl;
    }
    
    void afterDamage() override {
        if (health <= 0) {
            std::cout << "전사 사망" << std::endl;
        }
    }
};

예시 4: 인터페이스 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class IDatabase {
public:
    virtual void connect() = 0;
    virtual void disconnect() = 0;
    virtual void query(const std::string& sql) = 0;
    virtual ~IDatabase() = default;
};
class MySQLDatabase : public IDatabase {
public:
    void connect() override {
        std::cout << "MySQL 연결" << std::endl;
    }
    
    void disconnect() override {
        std::cout << "MySQL 연결 해제" << std::endl;
    }
    
    void query(const std::string& sql) override {
        std::cout << "MySQL 쿼리: " << sql << std::endl;
    }
};
class PostgreSQLDatabase final : public IDatabase {
public:
    void connect() override {
        std::cout << "PostgreSQL 연결" << std::endl;
    }
    
    void disconnect() override {
        std::cout << "PostgreSQL 연결 해제" << std::endl;
    }
    
    void query(const std::string& sql) override final {
        std::cout << "PostgreSQL 쿼리: " << sql << std::endl;
    }
};

override와 const

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func() const {}
};
class Derived : public Base {
public:
    // ❌ const 누락 (새 함수)
    void func() {}
    
    // ✅ override (에러 발견)
    void func() override {}  // 에러: const 불일치
    
    // ✅ 올바른 오버라이드
    void func() const override {}
};

자주 발생하는 문제

문제 1: 오타

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void process() {}
};
class Derived : public Base {
public:
    // ❌ 오타 (새 함수 생성)
    void proccess() {}  // 오타!
    
    // ✅ override (에러 발견)
    void proccess() override {}  // 에러
};

문제 2: 시그니처 불일치

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func(int x) {}
};
class Derived : public Base {
public:
    // ❌ 매개변수 타입 다름
    void func(double x) {}
    
    // ✅ override (에러 발견)
    void func(double x) override {}  // 에러
};

문제 3: final 상속 시도

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base final {
public:
    void func() {}
};
// ❌ final 클래스 상속
// class Derived : public Base {};  // 에러
// ✅ 컴포지션 사용
class Derived {
private:
    Base base;
    
public:
    void func() {
        base.func();
    }
};

문제 4: final 함수 오버라이드

아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func() final {}
};
class Derived : public Base {
public:
    // ❌ final 함수 오버라이드
    // void func() override {}  // 에러
};

override와 final 조합

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func1() {}
    virtual void func2() {}
};
class Middle : public Base {
public:
    void func1() override {
        // 오버라이드만
    }
    
    void func2() override final {
        // 오버라이드하고 더 이상 오버라이드 금지
    }
};
class Derived : public Middle {
public:
    void func1() override {
        // OK
    }
    
    // void func2() override {}  // 에러: final
};

성능 고려사항

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// final 클래스: devirtualization 가능
class FinalClass final {
public:
    virtual void func() {
        // 컴파일러가 직접 호출로 최적화 가능
    }
};
// final 함수: 인라인 가능
class Base {
public:
    virtual void func() final {
        // 더 이상 오버라이드 없음 → 인라인 가능
    }
};

성능 영향: vtable, devirtualization, 측정 시 유의점

  • 일반적인 가상 호출: 정적 분석만으로는 대상 함수가 확정되지 않으면 간접 분기(vtable 경유)가 됩니다. 나노초 단위라도 핫 루프에서는 누적될 수 있습니다.
  • final / final class: 파생이 없다는 사실이 알려지면 컴파일러는 직접 호출로 바꾸거나 인라인할 여지가 커집니다(구현·최적화 수준 의존, UB 없이 가능한 경우).
  • override 자체: 컴파일 타임 표식이라 런타임 비용은 없습니다. 오히려 잘못된 오버라이드를 막아 불필요한 가상 호출을 줄이는 데 도움이 됩니다. 실무: final을 성능 때문에 붙이기 전에 프로파일로 병목이 가상 호출인지 확인하는 것이 안전합니다. API를 닫는 것은 설계·유지보수와의 트레이드오프가 더 큽니다.

실무 패턴

패턴 1: 인터페이스 + final 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 인터페이스 (순수 가상 함수)
class IPaymentProcessor {
public:
    virtual void processPayment(double amount) = 0;
    virtual bool validateCard(const std::string& cardNumber) = 0;
    virtual ~IPaymentProcessor() = default;
};
// final 구현 (더 이상 확장 불필요)
class SecurePaymentProcessor final : public IPaymentProcessor {
public:
    void processPayment(double amount) override {
        if (validateCard(currentCard_)) {
            // 결제 처리
            std::cout << "결제 완료: $" << amount << '\n';
        }
    }
    
    bool validateCard(const std::string& cardNumber) override final {
        // 보안 검증 로직 (변경 금지)
        return cardNumber.length() == 16;
    }
    
private:
    std::string currentCard_;
};

패턴 2: 템플릿 메서드 + final

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class DataProcessor {
public:
    // 템플릿 메서드 (변경 금지)
    void process() final {
        loadData();
        validateData();
        transformData();
        saveData();
    }
    
    virtual ~DataProcessor() = default;
    
protected:
    virtual void loadData() = 0;
    virtual void validateData() = 0;
    virtual void transformData() = 0;
    virtual void saveData() = 0;
};
class CSVProcessor : public DataProcessor {
protected:
    void loadData() override {
        std::cout << "CSV 로드\n";
    }
    
    void validateData() override {
        std::cout << "CSV 검증\n";
    }
    
    void transformData() override {
        std::cout << "CSV 변환\n";
    }
    
    void saveData() override {
        std::cout << "CSV 저장\n";
    }
};

패턴 3: 계층적 final

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void step1() {}
    virtual void step2() {}
    virtual void step3() {}
    virtual ~Base() = default;
};
class Middle : public Base {
public:
    void step1() override final {
        // step1은 더 이상 오버라이드 불가
    }
    
    void step2() override {
        // step2는 계속 오버라이드 가능
    }
};
class Derived : public Middle {
public:
    // void step1() override {}  // 에러: final
    
    void step2() override {
        // OK
    }
    
    void step3() override {
        // OK
    }
};

FAQ

Q1: override는 언제 사용해야 하나요?

A: 가상 함수를 오버라이드할 때 항상 사용하세요. 오타, 시그니처 불일치, const 누락 등을 컴파일 타임에 감지합니다. 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void process(int x) const {}
};
class Derived : public Base {
public:
    // ✅ override 사용: 모든 오류 감지
    void process(int x) const override {}
    
    // ❌ override 없음: 오류 발견 못함
    // void proccess(int x) const {}  // 오타
    // void process(int x) {}         // const 누락
    // void process(double x) const {} // 타입 다름
};

Q2: final은 언제 사용해야 하나요?

A:

  • 더 이상 상속/오버라이드가 불필요할 때: 설계가 완성됨
  • 보안/안정성이 중요할 때: 중요한 로직 보호
  • 성능 최적화가 필요할 때: devirtualization 활용 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 보안: 인증 로직 보호
class AuthenticationManager final {
    // 더 이상 상속 불가
};
// 성능: 최적화 가능
class HighPerformanceClass {
public:
    virtual void criticalPath() final {
        // devirtualization 가능
    }
};

Q3: override 없이도 동작하나요?

A: , 동작합니다. 하지만 override를 사용하는 것이 강력히 권장됩니다. 안전성과 명확성을 크게 향상시킵니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func() {}
};
class Derived : public Base {
public:
    // 동작하지만 권장하지 않음
    void func() {}
    
    // 권장: override 사용
    void func() override {}
};

Q4: final의 성능 이점은?

A: devirtualization인라인 최적화가 가능합니다. 가상 함수 호출 오버헤드(vtable 조회)를 제거할 수 있습니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func() final {
        // 컴파일러가 직접 호출로 최적화 가능
    }
};
// 호출 시
Base* ptr = new Base();
ptr->func();  // vtable 조회 없이 직접 호출 가능

성능 비교:

  • 일반 가상 함수: vtable 조회 + 간접 호출 (~5-10ns)
  • final 함수: 직접 호출 + 인라인 가능 (~0ns)

Q5: override와 virtual의 차이는?

A:

  • virtual: 기반 클래스에서 가상 함수 선언
  • override: 파생 클래스에서 오버라이드 명시 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
class Base {
public:
    virtual void func() {}  // virtual: 가상 함수 선언
};
class Derived : public Base {
public:
    void func() override {}  // override: 오버라이드 명시
};

함께 사용할 수 없음: 파생 클래스에서 virtual override는 불필요합니다.

Q6: override와 final을 함께 사용할 수 있나요?

A: , 가능합니다. 기반 클래스 함수를 오버라이드하면서 동시에 더 이상의 오버라이드를 금지합니다. 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Base {
public:
    virtual void func() {}
};
class Middle : public Base {
public:
    void func() override final {
        // 오버라이드하고 더 이상 오버라이드 금지
    }
};
class Derived : public Middle {
public:
    // void func() override {}  // 에러: final
};

Q7: final 클래스를 사용하면 어떤 단점이 있나요?

A: 확장성 제한이 주요 단점입니다. 나중에 기능을 확장하고 싶어도 불가능합니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.

class FinalClass final {
    // 좋은 설계였지만...
};
// 나중에 확장이 필요해도 불가능
// class ExtendedClass : public FinalClass {};  // 에러

권장: 정말 확실할 때만 final 사용. 불확실하면 final 없이 설계.

Q8: override/final 학습 리소스는?

A:


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3