Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++와 Java의 기본 가상 함수 동작 차이 완벽 정리


C++와 Java의 가상 함수 기본 동작 차이

C++와 Java는 모두 객체 지향 프로그래밍 언어이지만, 메서드가 기본적으로 가상(virtual)인지 여부에서 정반대의 철학을 보입니다. 이 차이는 다형성(polymorphism)을 활용하는 코드의 실행 결과에 직접적인 영향을 주기 때문에, 두 언어를 함께 다루는 개발자라면 반드시 이해해야 할 핵심 개념입니다.

C++: 메서드는 기본적으로 비가상(non-virtual)

C++에서 메서드는 기본적으로 비가상(non-virtual)입니다. 따라서 기본 클래스의 포인터나 참조가 파생 클래스 객체를 가리키고 있더라도, 오버라이딩된 파생 클래스의 메서드가 아니라 기본 클래스의 메서드가 호출됩니다. 가상 함수로 만들려면 함수 선언 앞에 virtual 키워드를 명시적으로 붙여야 합니다.

#include <iostream>
using namespace std;

class B {
public:
    void s() { // 기본적으로 비가상. virtual을 붙이면 "In Derived"가 출력됨
        cout << "In Base \n";
    }
};

class D : public B {
public:
    void s() {
        cout << "In Derived \n";
    }
};

int main(void) {
    D d;         // D 클래스의 객체
    B &b = d;    // 기본 클래스 참조가 파생 객체를 가리킴
    b.s();       // s()가 비가상이므로 B::s() 호출
    return 0;
}

실행 결과

In Base

s()가 가상 함수가 아니기 때문에 컴파일러는 참조 변수의 정적 타입(B)을 기준으로 호출 대상을 결정합니다. 만약 virtual void s()처럼 선언했다면, 런타임에 실제 객체의 타입을 확인하여 "In Derived"가 출력됩니다.

Java: 메서드는 기본적으로 가상(virtual)

반면 Java의 모든 인스턴스 메서드는 기본적으로 가상(virtual)입니다. 별도의 키워드 없이도 오버라이딩된 메서드가 런타임에 실제 객체 타입에 따라 선택되어 호출됩니다. 특정 메서드의 오버라이딩을 금지하고 싶다면 final 키워드를 붙여 비가상처럼 동작하게 만들 수 있습니다.

class B {
    public void s() {
        System.out.println("In Base"); // 기본적으로 가상
    }
}

class D extends B {
    @Override
    public void s() {
        System.out.println("In Derived");
    }
}

public class Main {
    public static void main(String[] args) {
        B b = new D(); // 업캐스팅
        b.s();         // 런타임에 D의 s()가 호출됨
    }
}

실행 결과

In Derived

참조 변수의 선언 타입이 B이더라도, 실제 생성된 객체가 D이기 때문에 런타임에는 Ds()가 호출됩니다. 이것이 Java의 기본 동작인 동적 디스패치(dynamic dispatch)입니다.

핵심 비교 정리

구분C++Java
메서드 기본 동작비가상(non-virtual)가상(virtual)
가상으로 만들기virtual 키워드 필요기본값 (키워드 불필요)
비가상으로 만들기기본값 (키워드 불필요)final 키워드 필요
바인딩 시점비가상: 컴파일 타임 / 가상: 런타임런타임 (final 제외)

결론적으로 C++는 성능과 명시적인 설계를 중시하는 반면, Java는 직관적이고 안전한 다형성을 기본 동작으로 채택했습니다. 이러한 언어 철학의 차이를 이해하면 다형성 관련 버그를 예방하고 더 견고한 코드를 작성하는 데 큰 도움이 됩니다.