맨위로가기

동적 디스패치

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

동적 디스패치는 프로그램 실행 중에 호출될 메서드 또는 함수를 결정하는 방식으로, 컴파일 시간에 결정되는 정적 디스패치와 대조된다. 단일 객체의 유형에 따라 메서드를 선택하는 단일 디스패치와 여러 객체의 조합에 따라 메서드를 선택하는 다중 디스패치가 존재한다. 동적 디스패치는 언어에 따라 다양한 메커니즘으로 구현되며, 인수의 유형, 특히 수신자 유형을 기반으로 수행되는 경우가 많다. C++, Go, Rust, Nim, Smalltalk, Python 등 다양한 언어에서 동적 디스패치를 구현하며, 각 언어는 고유한 방식과 성능 특징을 가진다.

더 읽어볼만한 페이지

  • 메소드 (컴퓨터 프로그래밍) - 소멸자 (컴퓨터 프로그래밍)
    소멸자는 객체가 메모리에서 제거되기 직전에 호출되는 멤버 함수로, 객체 자원 해제 및 정리 작업을 수행하며, C++ 등 여러 언어에서 구현되고 메모리 누수 방지에 기여한다.
  • 메소드 (컴퓨터 프로그래밍) - 생성자
    생성자는 객체 지향 프로그래밍에서 클래스의 인스턴스를 생성하고 초기화하며, 클래스 기반 언어에서는 클래스명과 동일한 이름을 가지고, JavaScript에서는 `new` 연산자와 함께 함수를 호출하거나 `class` 키워드를 통해 정의한다.
동적 디스패치
동적 디스패치
유형다형성 메커니즘
설명실행 시간에 어떤 메서드 또는 함수 구현을 호출할지 선택하는 과정
다른 이름실행 시간 디스패치
실행 시간 바인딩
관련 개념가상 함수
이중 디스패치
다중 디스패치
술어 디스패치

2. 동적 디스패치의 개념

동적 디스패치는 프로그램 실행 중에 호출할 메서드나 함수가 결정되는 방식이다. 이는 컴파일 시간에 결정되는 정적 디스패치와 대비된다.

2. 1. 단일 및 다중 디스패치

호출할 메서드의 버전을 선택하는 것은 단일 객체 또는 객체의 조합을 기반으로 할 수 있다. 전자를 '''단일 디스패치'''라고 하며, 스몰토크, C++, 자바, C#, Objective-C, 스위프트, 자바스크립트, 파이썬과 같은 일반적인 객체 지향 언어에서 직접 지원된다. 이러한 언어 및 유사한 언어에서는 다음과 같은 구문을 사용하여 나눗셈 메서드를 호출할 수 있다.

```python

dividend.divide(divisor) # dividend / divisor

```

여기서 매개변수는 선택 사항이다. 이는 `dividend`에 `divisor` 매개변수를 사용하여 `divide`라는 메시지를 보내는 것으로 간주된다. 구현은 `divisor`의 유형이나 값과 관계없이, 오직 `dividend`의 유형(예: 유리수, 부동 소수점, 행렬)만을 기반으로 선택된다.

반대로, 일부 언어는 피연산자의 조합을 기반으로 메서드 또는 함수를 디스패치한다. 나눗셈의 경우, `dividend`와 `divisor`의 유형이 함께 어떤 `divide` 연산을 수행할지 결정한다. 이를 '''다중 디스패치'''라고 한다. 다중 디스패치를 지원하는 언어의 예로는 Common Lisp, 딜런, 줄리아가 있다.

2. 2. 동적 디스패치의 메커니즘

언어는 다양한 동적 디스패치 메커니즘으로 구현될 수 있다. 언어에서 제공하는 동적 디스패치 메커니즘의 선택은 해당 언어 내에서 사용할 수 있거나 가장 자연스러운 프로그래밍 패러다임을 상당 부분 변경한다.

일반적으로, 형식이 지정된 언어에서 디스패치 메커니즘은 인수의 유형을 기반으로 수행된다(가장 일반적으로 메시지의 수신자 유형을 기반으로). 약한 타입 또는 무타입 언어는 각 객체에 대한 객체 데이터의 일부로 디스패치 테이블을 포함하는 경우가 많다. 이를 통해 각 인스턴스가 주어진 메시지를 별도의 메서드에 매핑할 수 있으므로 '''인스턴스 동작'''이 가능하다.

일부 언어는 하이브리드 방식을 제공한다.

3. 주요 언어별 구현 방식

C++, Go, Rust, Nim, Smalltalk, 파이썬 등 여러 프로그래밍 언어에서 동적 디스패치가 구현되는 방식을 살펴본다.


  • C++: 가상 함수 테이블(vtable)을 사용하여 동적 디스패치를 구현한다. `virtual` 키워드로 선언된 메서드는 런타임에 객체의 실제 타입에 따라 호출될 메서드가 결정된다. C++는 정적 디스패치도 지원하며, 기본적으로 정적 디스패치를 사용한다.[1]
  • Go, Rust, Nim: '팻 포인터' (Go의 '인터페이스', Rust의 '트레이트 객체')를 사용하여 가상 함수 테이블 포인터를 객체 참조와 함께 전달한다.[1] 이를 통해 기본 데이터 구조와 지원되는 인터페이스를 분리할 수 있다.
  • Smalltalk: 타입 기반 메시지 디스패처를 사용한다. 인스턴스가 메시지를 받으면, 디스패처는 해당 타입의 메시지-메서드 맵에서 메서드를 찾아 호출한다. 인라인 캐싱과 같은 기술을 사용하여 메서드 디스패치 속도를 높인다.
  • 파이썬, 루비, Objective-C, Groovy: Smalltalk과 유사한 접근 방식을 사용하며, 동적으로 타입이 지정되는 언어의 특징을 활용한다.


각 언어별 구현 방식은 성능, 유연성, 복잡성 측면에서 장단점을 가진다. 예를 들어 C++는 가상 함수 테이블을 통해 효율적인 동적 디스패치를 제공하지만, 다중 상속 시 복잡성이 증가할 수 있다. Go, Rust, Nim은 팻 포인터를 사용하여 유연성을 높였지만, 객체 참조에 추가 데이터가 필요하다는 단점이 있다. Smalltalk는 인라인 캐싱을 통해 동적 디스패치 오버헤드를 줄이지만, 단순한 구현은 C++보다 느릴 수 있다.

3. 1. C++ 구현

C++는 초기 바인딩을 사용하며 동적 디스패치와 정적 디스패치를 모두 제공한다. 디스패치의 기본 형태는 정적이다. 동적 디스패치를 사용하려면 프로그래머는 메서드를 `virtual`로 선언해야 한다.

C++ 컴파일러는 일반적으로 주어진 클래스에 대한 이름-구현 매핑을 멤버 함수 포인터 집합으로 정의하는 가상 함수 테이블(vtable)이라는 데이터 구조를 사용하여 동적 디스패치를 구현한다. 이는 C++ 사양에서 vtable을 언급하지 않으므로 순전히 구현 세부 사항일 뿐이다. 해당 유형의 인스턴스는 인스턴스 데이터의 일부로 이 테이블에 대한 포인터를 저장하며, 다중 상속을 사용하는 시나리오를 복잡하게 만든다. C++는 후기 바인딩을 지원하지 않으므로 C++ 객체의 가상 테이블은 런타임에 수정할 수 없으며, 이는 디스패치 대상의 잠재적 집합을 컴파일 시간에 선택된 유한한 집합으로 제한한다.

타입 오버로딩은 C++에서 동적 디스패치를 생성하지 않는데, 언어는 메시지 매개변수의 타입을 공식 메시지 이름의 일부로 간주하기 때문이다. 즉, 프로그래머가 보는 메시지 이름은 바인딩에 사용되는 공식 이름이 아니다.

3. 2. Go, Rust, Nim 구현

Go, Rust, Nim은 초기 바인딩의 더 다재다능한 변형을 사용한다. 가상 함수 테이블 포인터는 객체 참조와 함께 '팻 포인터'(Go의 '인터페이스' 또는 Rust의 '트레이트 객체'[1])로 전달된다.

이는 지원되는 인터페이스를 기본 데이터 구조와 분리한다. 각 컴파일된 라이브러리는 형식을 올바르게 사용하기 위해 지원되는 전체 인터페이스 범위를 알 필요가 없으며, 필요한 특정 가상 함수 테이블 레이아웃만 알면 된다. 코드는 동일한 데이터 조각에 대한 다른 인터페이스를 다른 함수로 전달할 수 있다. 이러한 다재다능함에는 각 객체 참조에 추가 데이터가 필요하다는 단점이 있으며, 이러한 참조가 많이 영구적으로 저장되는 경우 문제가 될 수 있다.

'팻 포인터'라는 용어는 단순히 추가 정보가 연관된 포인터를 의미한다.

3. 3. Smalltalk 구현

Smalltalk는 타입 기반 메시지 디스패처를 사용한다. 각 인스턴스는 메서드를 포함하는 정의를 가진 단일 타입을 갖는다. 인스턴스가 메시지를 받으면 디스패처는 해당 타입의 메시지-메서드 맵에서 해당 메서드를 찾아 호출한다.

타입은 기본 타입의 체인을 가질 수 있으므로 이 조회는 비용이 많이 들 수 있다. Smalltalk 메커니즘의 단순한 구현은 C++보다 훨씬 더 높은 오버헤드를 가질 수 있으며, 이 오버헤드는 객체가 받는 모든 메시지에 대해 발생한다.

실제 Smalltalk 구현은 종종 인라인 캐싱이라고 하는 기술을 사용하여 메서드 디스패치를 매우 빠르게 만든다. 인라인 캐싱은 기본적으로 호출 사이트의 이전 대상 메서드 주소와 객체 클래스를 저장한다(다중 캐싱의 경우 여러 쌍). 캐시된 메서드는 메서드 선택기를 기반으로 가장 일반적인 대상 메서드(또는 캐시 미스 핸들러)로 초기화된다. 실행 중에 메서드 호출 사이트에 도달하면 캐시의 주소를 호출한다. (동적 코드 생성기에서 이 호출은 직접 주소가 캐시 미스 로직에 의해 백 패치되므로 직접 호출이다.) 그런 다음 호출된 메서드의 프롤로그 코드는 캐시된 클래스와 실제 객체 클래스를 비교하고, 일치하지 않으면 실행은 클래스에서 올바른 메서드를 찾기 위해 캐시 미스 핸들러로 분기한다. 빠른 구현은 여러 캐시 항목을 가질 수 있으며 초기 캐시 미스 시 올바른 메서드로 실행을 가져오는 데 단 몇 개의 명령만 필요하다. 일반적인 경우는 캐시된 클래스 일치이며 실행은 메서드에서 계속된다.

아웃오브라인 캐싱은 객체 클래스와 메서드 선택기를 사용하여 메서드 호출 로직에서도 사용할 수 있다. 한 설계에서는 클래스와 메서드 선택기를 해시하여 메서드 디스패치 캐시 테이블의 인덱스로 사용한다.

Smalltalk는 반사적인 언어이므로 많은 구현에서 개별 객체를 동적으로 생성된 메서드 조회 테이블을 가진 객체로 변형할 수 있다. 이를 통해 객체별로 객체 동작을 변경할 수 있다. 프로토타입 기반 프로그래밍으로 알려진 전체 언어 범주가 여기에서 성장했으며, 가장 유명한 언어는 Self와 자바스크립트이다. 메서드 디스패치 캐싱을 신중하게 설계하면 프로토타입 기반 언어도 고성능 메서드 디스패치를 가질 수 있다.

파이썬, 루비, Objective-C, Groovy를 포함한 많은 다른 동적으로 타입이 지정된 언어에서도 유사한 접근 방식을 사용한다.

3. 4. 파이썬 구현 예제

python

class 고양이:

def 말하다(self):

print("야옹")

class 개:

def 말하다(self):

print("멍멍")

def 말하다(애완동물):

# speak 메서드를 동적으로 디스패치한다.

# 애완동물은 고양이 또는 개의 인스턴스일 수 있다.

애완동물.말하다()

고양이 = 고양이()

말하다(고양이)

개 = 개()

말하다(개)

3. 5. C++ 구현 예제

cpp

#include

// Pet을 추상 가상 기본 클래스로 만듭니다.

class Pet {

public:

virtual void speak() = 0;

};

class Dog : public Pet {

public:

void speak() override

{

std::cout << "Woof!\n";

}

};

class Cat : public Pet {

public:

void speak() override

{

std::cout << "Meow!\n";

}

};

// speak()는 Pet에서 파생된 모든 것을 받아들일 수 있습니다.

void speak(Pet& pet)

{

pet.speak();

}

int main()

{

Dog fido;

Cat simba;

speak(fido);

speak(simba);

return 0;

}

```

C++에서 동적 디스패치는 가상 함수를 통해 구현된다. 위 코드는 `Pet` 추상 클래스를 정의하고, `Dog`와 `Cat` 클래스가 `Pet` 클래스를 상속받아 `speak()` 가상 함수를 재정의(override)하는 예시를 보여준다. `speak()` 함수는 `Pet` 타입의 참조자를 매개변수로 받아서, 실제 객체의 타입에 따라 `Dog` 또는 `Cat` 클래스의 `speak()` 함수를 호출한다. `main()` 함수에서는 `Dog` 객체 `fido`와 `Cat` 객체 `simba`를 생성하고, `speak()` 함수에 전달하여 동적 디스패치가 어떻게 작동하는지 보여준다.[1]


본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com