[C/C++] 정적 바인딩(Static Binding), 동적 바인딩(Dynamic Binding)

할랑말랑·2026년 3월 4일

C/C++

목록 보기
1/45

바인딩(Binding)

이름(식별자)과 실제 값(객체, 함수 등)을 연결하는 과정

  • 이름과 실제 메모리 위치/값을 연결
  • 변수 이름이 어떤 메모리 주소를 가리킬지 결정
  • 함수 호출 시 어떤 함수가 실행될지 결정

1. 정적 바인딩(Static Binding)

  • 컴파일 타임에 호출될 함수나 변수의 주소가 결정되는 방식
  • 프로그램 실행 전에 이미 모든 것이 정해짐
  • Early Binding 또는 Compile-time Binding이라고도 함

1-1. 특징

  • 컴파일러가 코드를 보고 어떤 함수를 호출할지 미리 결정
  • 실행 속도가 빠름
  • 유연성은 낮음
  • 일반 함수 호출, 함수 오버로딩이 정적 바인딩
#include <iostream>
using namespace std;

class Animal {
public:
    void speak() { // virtual 없음
        cout << "동물 소리" << endl;
    }

    void eat() {
        cout << "동물이 먹습니다" << endl;
    }
};

class Dog : public Animal {
public:
    void speak() { // 함수 hiding (오버라이딩 아님!)
        cout << "멍멍!" << endl;
    }

    void eat() {
        cout << "개가 사료를 먹습니다" << endl;
    }
};

int main() {
    cout << "=== 직접 객체 사용 ===" << endl;
    Animal a;
    Dog d;

    a.speak(); // Animal::speak() - 정적 바인딩
    d.speak(); // Dog::speak() - 정적 바인딩

    cout << "\n=== 포인터 사용 ===" << endl;
    Animal* ptr1 = new Animal();
    Animal* ptr2 = new Dog(); // Dog 객체지만 Animal* 타입

    ptr1->speak(); // Animal::speak() - 정적 바인딩
    ptr2->speak(); // Animal::speak() - 정적 바인딩 (포인터 타입 기준!)

    ptr1->eat(); // Animal::eat()
    ptr2->eat(); // Animal::eat() (Dog::eat()이 호출되지 않음!)

    delete ptr1;
    delete ptr2;

    return 0;
}

1-2. 정적 바인딩 예시

  • 일반 함수 호출
  • 함수 오버로딩
  • 포인터/참조 타입에 따라 결정
  • virtual 키워드 없는 멤버 함수

1-3. 장점

  • 성능이 좋음 (컴파일 시 최적화 가능)
  • 타입 안정성이 높음
  • 에러를 컴파일 시점에 발견 가능

1-4. 단점

  • 실행 중 동작을 변경할 수 없음
  • 다형성 구현이 제한적

2. 동적 바인딩(Dynamic Binding)

  • 런타임(실행 시간)에 바인딩이 결정됨
  • 프로그램이 실행되는 동안 결정됨
  • Late Binding 또는 Runtime Binding이라고도 함

2-1. 특징

  • 실제 객체의 타입에 따라 호출될 함수가 결정됨
  • 가상 함수(virtual function)를 통해 구현
  • 다형성의 핵심 메커니즘
  • 실행 시점에 결정되므로 유연함
#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() { // virtual 추가!
        cout << "동물 소리" << endl;
    }

    virtual void eat() {
        cout << "동물이 먹습니다" << endl;
    }

    virtual ~Animal() {
        cout << "Animal 소멸" << endl;
    }
};

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

    void eat() override {
        cout << "개가 사료를 먹습니다" << endl;
    }

    ~Dog() {
        cout << "Dog 소멸" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() override {
        cout << "야옹~" << endl;
    }

    void eat() override {
        cout << "고양이가 생선을 먹습니다" << endl;
    }

    ~Cat() {
        cout << "Cat 소멸" << endl;
    }
};

int main() {
    cout << "=== 직접 객체 사용 ===" << endl;
    Animal a;
    Dog d;
    Cat c;

    a.speak(); // Animal::speak()
    d.speak(); // Dog::speak()
    c.speak(); // Cat::speak()

    cout << "\n=== 포인터 사용 (동적 바인딩) ===" << endl;
    Animal* ptr1 = new Animal();
    Animal* ptr2 = new Dog();
    Animal* ptr3 = new Cat();

    ptr1->speak(); // Animal::speak() - 동적 바인딩
    ptr2->speak(); // Dog::speak() - 동적 바인딩 (실제 객체 확인!)
    ptr3->speak(); // Cat::speak() - 동적 바인딩

    ptr1->eat(); // Animal::eat()
    ptr2->eat(); // Dog::eat() (실제 타입에 따라 호출!)
    ptr3->eat(); // Cat::eat()

    cout << "\n=== 메모리 해제 ===" << endl;
    delete ptr1;
    delete ptr2; // Dog 소멸자 먼저, 그 다음 Animal 소멸자
    delete ptr3; // Cat 소멸자 먼저, 그 다음 Animal 소멸자

    return 0;
}

2-2. 동적 바인딩 예시

  • 가상 함수 (virtual function)
  • 포인터/참조가 가리키는 실제 객체에 따라 결정
  • 상속 관계에서 다형성 구현

2-3. 장점

  • 다형성 구현 가능
  • 유연하고 확장 가능한 코드
  • 객체지향 프로그래밍의 핵심

2-4. 단점

  • 정적 바인딩보다 느림 (vtable 조회 필요)
  • 약간의 메모리 오버헤드
  • 컴파일 시 최적화가 어려움

3. 가상 함수 테이블 (vtable)

  • 동적 바인딩을 구현하는 메커니즘
  • 각 클래스마다 가상 함수들의 주소를 담은 테이블
  • 객체는 vtable을 가리키는 포인터(vptr)를 가짐

동작방식

  • 가상 함수가 있는 클래스는 vtable 생성
  • 각 객체는 생성 시 vptr 초기화
  • 가상 함수 호출 시
  1. vptr을 통해 vtable 접근
  2. vtable에서 실제 함수 주소 찾기
  3. 해당 함수 실행

0개의 댓글