C++

[C++] 가상 함수 (virtual function)

코딩 메모장 2025. 4. 23. 23:13
728x90

[C++] 가상 함수 (virtual function)

가상 함수는 C++에서 다형성(polymorphism)을 구현하기 위한 핵심 개념입니다. 주로 상속 구조에서 기본 클래스 포인터(또는 참조)를 통해 파생 클래스의 함수를 호출할 수 있도록 도와줍니다.


1. 가상 함수란?

  • virtual 키워드를 사용하여 기본 클래스에 선언된 멤버 함수입니다.
  • 파생 클래스에서 재정의(overriding)할 수 있으며, 이를 통해 런타임 다형성(runtime polymorphism)이 가능합니다.
class Base {
public:
    virtual void show(); // 가상 함수
};

2. 런타임 다형성의 동작 원리

가상 함수는 동적 바인딩(dynamic binding)을 지원합니다. 즉, 컴파일 시점이 아닌 실행 시간에 어떤 함수가 호출될지를 결정합니다.

✅ 가상 함수 예제

class Base {
public:
    virtual void show() {
        std::cout << "Base class show function.\n";
    }
};

class Derived : public Base {
public:
    void show() override {
        std::cout << "Derived class show function.\n";
    }
};

void display(Base* base) {
    base->show(); // 런타임에 실제 객체 타입에 따라 호출
}

int main() {
    Base b;
    Derived d;
    display(&b); // Base class show function.
    display(&d); // Derived class show function.
}

3. 가상 함수가 아닐 때의 예제

class Base {
public:
    void show() { // 가상 함수가 아님
        std::cout << "Base class show function.\n";
    }
};

class Derived : public Base {
public:
    void show() {
        std::cout << "Derived class show function.\n";
    }
};

int main() {
    Base* base = new Derived();
    base->show(); // Base class show function. (정적 바인딩)
    delete base;
}

virtual 키워드가 없으면 컴파일 시점에 함수가 결정되며, 파생 클래스에서 오버라이딩해도 호출되지 않습니다.


4. 가상 소멸자

기본 클래스의 소멸자는 반드시 virtual로 선언하는 것이 좋습니다. 그렇지 않으면 파생 클래스 객체를 기본 클래스 포인터로 삭제할 때 파생 클래스 소멸자가 호출되지 않아 메모리 누수가 발생할 수 있습니다.

class Base {
public:
    virtual ~Base() { std::cout << "Base destructor\n"; }
};

class Derived : public Base {
public:
    ~Derived() { std::cout << "Derived destructor\n"; }
};

int main() {
    Base* ptr = new Derived();
    delete ptr; // Derived destructor → Base destructor (정상 호출)
}

5. 순수 가상 함수와 추상 클래스

순수 가상 함수는 함수 정의 없이 = 0으로 선언합니다. 해당 함수가 하나라도 있으면 추상 클래스(abstract class)가 되며, 이 클래스는 인스턴스를 생성할 수 없습니다.

class Animal {
public:
    virtual void speak() = 0; // 순수 가상 함수
};

class Dog : public Animal {
public:
    void speak() override {
        std::cout << "멍멍!\n";
    }
};

인터페이스처럼 사용되며, 파생 클래스에서 반드시 구현해야 합니다.


✅ 요약

항목 설명
virtual 함수 런타임에 함수 호출 결정 (동적 바인딩)
오버라이딩 파생 클래스에서 재정의 가능
가상 소멸자 파생 클래스 소멸자 호출 보장
순수 가상 함수 추상 클래스 생성용, 반드시 오버라이딩 필요
성능 일반 함수보다 약간 느리지만 다형성을 위해 필수
728x90