developer tip

C ++에서 동적 디스패치와 후기 바인딩의 차이점은 무엇입니까?

copycodes 2020. 11. 5. 08:13
반응형

C ++에서 동적 디스패치와 후기 바인딩의 차이점은 무엇입니까?


최근 Wikipedia 에서 Dynamic Dispatch에 대해 읽었 으며 C ++에서 동적 디스패치와 후기 바인딩의 차이점을 이해할 수 없었습니다.

각 메커니즘이 언제 사용됩니까?

Wikipedia의 정확한 인용문 :

동적 디스패치는 후기 바인딩 (동적 바인딩이라고도 함)과 다릅니다. 작업 선택과 관련하여 바인딩은 이름을 작업과 연결하는 프로세스를 의미합니다. 디스 패칭은 이름이 참조하는 작업을 결정한 후 작업에 대한 구현을 선택하는 것을 의미합니다. 동적 디스패치를 ​​사용하면 이름이 컴파일 타임에 다형성 작업에 바인딩 될 수 있지만 구현은 런타임까지 선택되지 않습니다 (이는 C ++에서 동적 디스패치가 작동하는 방식입니다). 그러나 후기 바인딩은 이름이 참조하는 작업을 선택할 때까지 선택할 다형성 작업의 구현을 선택할 수 없기 때문에 동적 디스패치를 ​​의미합니다.


이것에 대한 상당히 괜찮은 대답은 실제로 programmers.stackexchange.com의 늦은 바인딩과 초기 바인딩에 대한 질문에 통합됩니다 .

간단히 말해서 후기 바인딩은 평가 객체 측을 의미하고 동적 디스패치는 기능 측을 의미합니다. 늦게 바인딩에서 유형 변수를 런타임에 변형이다. 동적 디스패치에서 실행되는 함수 또는 서브 루틴은 변형입니다.

C ++에서는 유형 이 알려져 있기 때문에 실제로 후기 바인딩 없습니다 (상속 계층 구조의 끝일 필요는 없지만 최소한 공식적인 기본 클래스 또는 인터페이스). 그러나 우리 가상 방법과 다형성을 통한 동적 디스패치를 ​​가지고 있습니다.

후기 바인딩에 대해 제가 제공 할 수있는 가장 좋은 예는 Visual Basic의 형식화되지 않은 "개체"입니다. 런타임 환경은 모든 늦은 바인딩의 무거운 작업을 수행합니다.

Dim obj

- initialize object then..
obj.DoSomething()

컴파일러는 실제로 런타임 엔진이라는 메서드의 명명 된 조회를 수행하기 위해 적절한 실행 컨텍스트를 코딩하고, 적절 DoSomething하게 일치하는 매개 변수로 발견되면 실제로 기본 호출을 실행합니다. 실제로 객체의 유형에 대한 무언가 가 알려져 있습니다 (에서 상속 IDispatch및 지원 GetIDsOfNames()등). 하지만 멀리로 언어가 우려 유형 변수를 컴파일시에 완전히 알 수 있으며, 경우에 아무 생각이 없다 DoSomething무엇 이건 심지어 방법입니다 obj사실 이다 런타임이 실행의 지점에 도달 할 때까지.

나는 당신이 이미 그들이 어떻게 생겼는지 알고 있다고 확신하기 때문에 C ++ 가상 인터페이스 등을 버리지 않을 것입니다. 나는 C ++ 언어가 단순히 이것을 할 수 없다는 것이 명백하기를 바랍니다. 강력한 유형입니다. 그것은 할 수있는 (그리고 분명히 않습니다) 다형성 가상 메서드의 기능을 통해 동적 파견을한다.


늦은 바인딩은 런타임 중에 이름으로 메서드를 호출합니다. DLL에서 메서드를 가져 오는 경우를 제외하고는 실제로 C ++에이 기능이 없습니다.
이에 대한 예는 다음과 같습니다. GetProcAddress ()

동적 디스패치를 ​​사용하면 컴파일러는 메서드의 올바른 구현을 호출하기에 충분한 정보를 가지고 있습니다. 이것은 일반적으로 가상 테이블 을 생성하여 수행됩니다 .


링크 자체의 차이를 설명했다 :

동적 디스패치는 후기 바인딩 (동적 바인딩이라고도 함)과 다릅니다. 작업 선택과 관련하여 바인딩은 이름을 작업과 연결하는 프로세스를 의미합니다. 디스 패칭은 이름이 참조하는 작업을 결정한 후 작업에 대한 구현을 선택하는 것을 의미합니다.

동적 디스패치를 ​​사용하면 이름이 컴파일 타임에 다형성 작업에 바인딩 될 수 있지만 구현은 런타임까지 선택되지 않습니다 (이는 C ++에서 동적 디스패치가 작동하는 방식입니다). 그러나 후기 바인딩은 이름이 참조하는 작업을 선택할 때까지 선택할 다형성 작업의 구현을 선택할 수 없기 때문에 동적 디스패치를 ​​의미합니다.

그러나 그것들은 대부분 C ++에서 동일합니다. 가상 함수와 vtable에 의해 동적 디스패치를 ​​할 수 있습니다.

C ++는 초기 바인딩을 사용하며 동적 및 정적 디스패치를 ​​모두 제공합니다. 디스패치의 기본 형식은 정적입니다. 동적 디스패치를 ​​얻으려면 메서드를 가상으로 선언해야합니다.


C ++에서는 둘 다 동일합니다.

C ++에는 두 종류의 바인딩이 있습니다.

  • 정적 바인딩 — 컴파일 타임에 수행됩니다.
  • 동적 바인딩 — 런타임에 수행됩니다.

동적 바인딩은 런타임에 수행되므로 후기 바인딩 이라고도 하며 정적 바인딩은 초기 바인딩 이라고도합니다 .

동적 바인딩을 사용하는 C ++는 가상 함수 (또는 함수 포인터 )를 통해 런타임 다형성을 지원 하고 정적 바인딩을 사용하면 다른 모든 함수 호출이 해결됩니다.


바인딩 은 이름을 작업과 연결하는 프로세스를 나타냅니다.

여기서 가장 중요한 것은 함수 매개 변수로 런타임에 호출 할 함수를 결정합니다.

디스 패칭 은 이름이 참조하는 작업을 결정한 후 작업에 대한 구현을 선택하는 것을 의미합니다.

매개 변수 일치에 따라 제어를 전달합니다.

http://en.wikipedia.org/wiki/Dynamic_dispatch

이것이 당신을 도울 수 있기를 바랍니다


동일하지 않기 때문에 차이점의 예를 보여 드리겠습니다. 예, 동적 디스패치는 슈퍼 클래스에서 객체를 참조 할 때 올바른 방법을 선택할 수있게 해주지 만 그 마법은 해당 클래스 계층에 매우 구체적이며 기본 클래스에서 일부 선언을 수행해야 작동합니다 (추상 메서드 테이블의 메소드 색인은 특정 유형간에 변경 될 수 없으므로 vtables를 작성하십시오. 따라서 Tabby 및 Lion 및 Tiger의 메서드를 모두 일반 Cat 포인터로 호출 할 수 있으며 Lions 및 Tigers 및 Tabbys로 채워진 Cat 배열도 가질 수 있습니다. 메서드 가 런타임 (동적 디스패치)에 선택 되더라도 컴파일 타임에 객체의 vtable에서 참조 하는 인덱스 (정적 / 초기 바인딩)를 알고 있습니다.

이제 Lions, Tigers 및 Bears가 포함 된 배열을 구현해 보겠습니다! ((어머!)). Animal이라는 기본 클래스가 없다고 가정하면 C ++에서는 컴파일러가 공통 기본 클래스 없이는 동적 디스패치를 ​​허용하지 않기 때문에 수행해야 할 중요한 작업이 있습니다. vtable의 인덱스는 일치해야하며 관련없는 클래스 간에는 수행 할 수 없습니다. 시스템에있는 모든 클래스의 가상 메서드를 보유 할 수있을만큼 충분히 큰 vtable이 필요합니다. C ++ 프로그래머는 클래스 디자인에 대해 특정 방식으로 생각하도록 훈련 되었기 때문에 이것을 제한으로 보는 경우는 거의 없습니다. 나는 그것이 더 좋거나 나쁘다는 것을 말하는 것이 아닙니다.

후기 바인딩을 사용하면 런타임이 공통 기본 클래스없이이를 처리합니다. 일반적으로 디스패처에서 사용되는 캐시 시스템을 사용하여 클래스에서 메서드를 찾는 데 사용되는 해시 테이블 시스템이 있습니다. C ++에서 컴파일러는 모든 유형을 알고 있습니다. 후기 바인딩 언어에서 객체 자체는 유형을 알고 있습니다 (유형이없는 것이 아니라 객체 자체가 대부분의 경우에 정확히 누구인지 알고 있음). 즉, 원하는 경우 여러 유형의 개체 배열을 가질 수 있습니다 (라이온스, 호랑이 및 곰). 또한 메시지 전달 및 프로토 타이핑 (클래스를 변경하지 않고 객체별로 동작을 변경할 수 있음) 및 기타 모든 종류를 구현할 수 있으며 후기 바인딩을 지원하지 않는 언어보다 훨씬 더 유연하고 코드 오버 헤드를 줄일 수 있습니다. .

Ever program in Android and use findViewById()? You almost always end up casting the result to get the right type, and casting is basically lying to the compiler and giving up all the static type-checking goodness that is supposed to make static languages superior. Of course, you could instead have findTextViewById(), findEditTextById(), and a million others so that your return types match, but that is throwing polymorphism out the window; arguably the whole basis of OOP. A late-bound language would probably let you simply index by an ID, and treat it like a hash table and not care what the type was being indexed nor returned.

Here's another example. Let's say that you have your Lion class and its default behavior is to eat you when you see it. In C++, if you wanted to have a single "trained" lion, you need to make a new subclass. Prototyping would let you simply change the one or two methods of that particular Lion that need to be changed. It's class and type don't change. C++ can't do that. This is important since when you have a new "AfricanSpottedLion" that inherits from Lion, you can train it too. The prototyping doesn't change the class structure so it can be expanded. This is normally how these languages handle issues that normally require multiple inheritance, or perhaps multiple inheritance is how you handle a lack of prototyping.

참고로, Objective-C는 SmallTalk의 메시지 전달이 추가 된 C이고 SmallTalk는 원래 OOP이며 둘 다 위의 모든 기능과 그 이상에 늦게 바인딩됩니다. 후기 바인딩 언어는 마이크로 수준의 관점에서 약간 느릴 수 있지만 종종 매크로 수준에서 더 효율적인 방식으로 코드를 구조화 할 수 있으며 모두 기본 설정으로 귀결됩니다.


위키피디아의 단어 정의를 감안할 때 동적 디스패치를 ​​C ++의 후기 바인딩으로 분류하고 싶습니다.

struct Base {
    virtual void foo(); // Dynamic dispatch according to Wikipedia definition
    void bar();         // Static dispatch according to Wikipedia definition
};

대신 Wikipedia의 경우 늦은 바인딩은 C ++의 멤버에 대한 포인터 디스패치를 ​​의미하는 것 같습니다.

(this->*mptr)();

호출되는 작업 (구현뿐만 아니라)의 선택은 런타임에 수행됩니다.

그러나 C ++ 문헌에서는 late binding일반적으로 Wikipedia에서 동적 디스패치를 ​​호출하는 데 사용됩니다.


동적 디스패치는 virtualC ++ 에서 키워드 를 사용할 때 발생합니다 . 예를 들면 다음과 같습니다.

struct Base
{
    virtual int method1() { return 1; }
    virtual int method2() { return 2; } // not overridden
};

struct Derived : public Base
{
    virtual int method1() { return 3; }
}

int main()
{
    Base* b = new Derived;
    std::cout << b->method1() << std::endl;
}

3메서드가 동적으로 전달 되었기 때문에 인쇄됩니다 . C ++ 표준은 이것이 장면 뒤에서 정확히 어떻게 발생하는지 지정 하지 않도록 매우 신중 하지만 태양 아래의 모든 컴파일러는 동일한 방식으로 수행합니다. 각 다형성 유형 ( 가상 테이블 또는 vtable 이라고 함)에 대한 함수 포인터 테이블을 만들고 가상 메서드를 호출하면 "실제"메서드가 vtable에서 조회되고 해당 버전이 호출됩니다. 따라서 다음과 같은 의사 코드를 이미징 할 수 있습니다.

struct BaseVTable
{
    int (*_method1) () = &Base::method1; // real function address
    int (*_method2) () = &Base::method2;
};

struct DerivedVTable
{  
    int (*method) () = &Derived::method1;
    int (*method2) () = &Base::method2; // not overridden
};

In this way, the compiler can be sure that a method with a particular signature exists at compile time. However, at run-time, the call might actually be dispatched via the vtable to a different function. Calls to virtual functions are a tiny bit slower than non-virtual calls, because of the extra indirection step.


On the other hand, my understanding of the term late binding is that the function pointer is looked up by name at runtime, from a hash table or something similar. This is the way things are done in Python, JavaScript and (if memory serves) Objective-C. This makes it possible to add new methods to a class at run-time, which cannot directly be done in C++. This is particularly useful for implementing things like mixins. However, the downside is that the run-time lookup is generally considerably slower than even a virtual call in C++, and the compiler is not able to perform any compile-time type checking for the newly-added methods.


I suppose the meaning is when you have two classes B,C inherits the same father class A. so, pointer of the father (type A) can hold each of sons types. The compiler cannot know what the type holds in the pointer in certain time, because it can change during the program run.

There is special functions to determine what the type of certain object in certain time. like instanceof in java, or by if(typeid(b) == typeid(A))... in c++.


This question might help you.

Dynamic dispatch generally refers to multiple dispatch.

Consider the below example. I hope it might help you.

    class Base2;
    class Derived2; //Derived2 class is child of Base2
class Base1 {
    public:
        virtual void function1 (Base2 *);
        virtual void function1 (Derived2 *);
}

class Derived1: public Base1 {
    public:
    //override.
    virtual void function1(Base2 *);
    virtual void function1(Derived2 *);
};

Consider the case of below.

Derived1 * d = new Derived1;
Base2 * b = new Derived2;

//Now which function1 will be called.
d->function1(b);

It will call function1 taking Base2* not Derived2*. This is due to lack of dynamic multiple dispatch.

Late binding is one of the mechanism to implement dynamic single dispatch.


In C++, both dynamic dispatch and late binding is the same. Basically, the value of a single object determines the piece of code invoked at runtime. In languages like C++ and java dynamic dispatch is more specifically dynamic single dispatch which works as mentioned above. In this case, since the binding occurs at runtime, it is also called late binding. Languages like smalltalk allow dynamic multiple dispatch in which the runtime method is chosen at runtime based on the identities or values of more than one object.

In C++ we dont really have late binding, because the type information is known. Thus in the C++ or Java context, dynamic dispatch and late binding are the same. Actual/fully late binding, I think is in languages like python which is a method-based lookup rather than type based.

참고URL : https://stackoverflow.com/questions/20187587/what-is-the-difference-between-dynamic-dispatch-and-late-binding-in-c

반응형