在 c + + 中,克劳斯·福尔曼和后期绑定有什么区别?

我最近读了一些关于 维基百科的克劳斯·福尔曼,不明白在 c + + 中克劳斯·福尔曼和后期绑定的区别。

当每一个机制都被使用?

引自维基百科的原话:

克劳斯·福尔曼不同于后期绑定(也称为动态绑定)。在选择操作的上下文中,绑定指的是将名称与操作关联的过程。分派指的是在决定名称所引用的操作之后为该操作选择一个实现。使用克劳斯·福尔曼,名称可以在编译时绑定到一个多态操作,但是直到运行时才能选择实现(这就是克劳斯·福尔曼在 C + + 中的工作方式)。但是,后期绑定确实意味着动态分派,因为在选择名称所引用的操作之前,无法选择要选择多态操作的哪个实现。

30855 次浏览

在 C + + 中,两者是相同的。

在 C + + 中,有两种绑定:

  • 在编译时完成的静态绑定。
  • 在运行时完成的动态绑定。

由于动态绑定是在运行时完成的,所以也称为 后期装订,静态绑定有时也称为 早期绑定

使用动态绑定,C + + 通过 虚拟的函数(或 函数指针函数指针)支持运行时多态性,并使用静态绑定解析所有 其他函数调用。

我假设这个意思是当你有两个 B 类时,C 继承同一个父类 A,所以父类的指针(类型 A)可以保存每个子类型。编译器不能知道在特定时间内指针中包含的类型,因为它可能在程序运行期间发生更改。

有一些特殊的函数来确定在特定时间内某个对象的类型。如 java 中的 instanceof,或 c + + 中的 if(typeid(b) == typeid(A))...

对于这个问题,一个相当不错的答案实际上被纳入到了关于 Programmers.stackexchange.com上的迟绑定和早绑定的问题中。

简而言之,后期绑定指的是 eval 的 对象端,而后期绑定指的是功能克劳斯·福尔曼。在后期绑定中,变量的 类型是运行时的变量。在动态调度中,正在执行的函数或子例程是变量。

在 C + + 中,我们实际上没有后期绑定,因为类型 已经知道了(不一定是继承层次结构的末尾,但至少是一个正式的基类或接口)。但是我们 通过虚拟方法和多态性来获得克劳斯·福尔曼。

我能提供的最好的后期绑定示例是 VisualBasic 中的非类型化“对象”。后期装订的重活都是执行期函式库帮你干的。

Dim obj


- initialize object then..
obj.DoSomething()

The compiler will actually code the appropriate execution context for the runtime-engine to perform a named lookup of the method called DoSomething, and if discovered with the properly matching parameters, actually execute the underlying call. In reality, 什么东西 about the type of the object is known (it inherits from IDispatch and supports GetIDsOfNames(), etc). but as far as the 语言 is concerned the 类型 of the variable is utterly unknown at compile time, and it has no idea if DoSomething is even a method for whatever obj actually until runtime reaches the point of execution.

我不会费心去转储一个 C + + 虚拟接口等等,因为我相信你已经知道它们看起来像什么。我希望 C + + 语言显然不能做到这一点。它是强类型的。它通过多态虚拟方法特性实现克劳斯·福尔曼。

鉴于维基百科上冗长的定义,我很想将克劳斯·福尔曼归类为 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通常用于维基百科所谓的克劳斯·福尔曼。

后期绑定是在运行时按名称调用方法。 除了从 DLL 导入方法之外,c + + 中实际上并没有这种功能。
这方面的一个例子是: GetProcAddress 网站: http://msdn.microsoft.com/en-us/library/windows/desk/ms683212% 28v = vs. 85% 29.aspx()

有了克劳斯·福尔曼,编译器就有足够的信息来调用方法的正确实现。这通常是通过创建一个 < a href = “ http://en.wikipedia.org/wiki/Virtual _ method _ table”> Virtual table 来完成的。

这个 有个问题可能对你有帮助。

克劳斯·福尔曼一般指多分派。

考虑下面的例子,我希望它能帮助你。

    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 *);
};

考虑下面的情况。

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


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

由于缺乏动态多分派,它将以 Base2*而不是 Derived2*呼叫 function1

后期绑定是实现动态单一调度的机制之一。

链接本身解释了这种差异:

克劳斯·福尔曼不同于后期绑定(也称为动态绑定)。在选择操作的上下文中,绑定指的是将名称与操作关联的过程。分派指的是在决定名称所引用的操作之后为该操作选择一个实现。

还有

使用克劳斯·福尔曼,名称可以在编译时绑定到一个多态操作,但是直到运行时才能选择实现(这就是克劳斯·福尔曼在 C + + 中的工作方式)。但是,后期绑定确实意味着动态分派,因为在选择名称所引用的操作之前,无法选择要选择多态操作的哪个实现。

但是它们在 c + + 中几乎是等价的,你可以通过虚函数和 vtables 做一个克劳斯·福尔曼。

C + + 使用早期绑定,并提供动态和静态分派。分派的默认形式是静态的。为了获得克劳斯·福尔曼,你必须声明一个方法是虚拟的。

Binding 指的是将名称与操作关联的过程。

主要是函数参数这些参数决定了在运行时调用哪个函数

Dispatching 指的是在确定名称所引用的操作之后为该操作选择一个实现。

根据参数匹配对其进行调度控制

Http://en.wikipedia.org/wiki/dynamic_dispatch

希望这个能帮到你

在 C + + 中,dynamic dispatchlate binding是相同的。基本上,单个对象的值决定了在运行时调用的代码段。在像 C + + 和 java 这样的语言中,克劳斯·福尔曼是动态的单一分派,就像上面提到的那样。在这种情况下,由于绑定发生在运行时,因此也称为 late binding。像 Smalltalk 这样的语言允许动态多分派,其中运行时方法是根据多个对象的标识或值在运行时选择的。

在 C + + 中,我们没有真正的后期绑定,因为类型信息是已知的。因此,在 C + + 或 Java 上下文中,克劳斯·福尔曼和后期绑定是相同的。实际/完全后期绑定,我认为是在 Python 这样的语言中,它是基于方法而不是基于类型的查找。

克劳斯·福尔曼是当你在 C + + 中使用 virtual关键字时发生的事情,例如:

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 查找“ real”方法,并调用该版本。所以你可以想象这样的伪代码:

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
};

通过这种方式,编译器可以确保具有特定签名的方法在编译时存在。但是,在运行时,调用实际上可能通过 vtable 被分派到另一个函数。由于额外的间接步骤,对虚函数的调用要比非虚函数的调用慢一点点。


另一方面,我对 后期装订这个术语的理解是,函数指针是由 姓名在运行时从散列表或类似的东西中查找的。这就是 Python、 JavaScript 和 Objective-C (如果内存允许的话)的工作方式。这使得向类 在运行时间添加新方法成为可能,而这不能直接在 C + + 中完成。这对于实现混合类型之类的东西特别有用。但是,缺点是运行时查找通常比 C + + 中的虚拟调用慢得多,而且编译器不能为新添加的方法执行任何编译时类型检查。

让我给你举个例子,因为它们是不一样的。是的,当你通过超类引用一个对象的时候,克劳斯·福尔曼可以让你选择正确的方法,但是这种魔法对于类层次结构来说是非常特殊的,你必须在基类中做一些声明来使它工作(抽象方法填充 vtables,因为表中方法的索引不能在特定类型之间变化)。因此,你可以通过一个通用的 Cat 指针来调用 Tabby 和 Lion and Tiger 中的方法,甚至可以有一个充满了 Lion and Tiger and Tabbys 的 Cat 数组。它知道这些方法在编译时(静态/早期绑定)在对象的 vtable 中引用的是什么,即使在运行时(克劳斯·福尔曼)选择了 方法

Now, lets implement an array that contains Lions and Tigers and Bears! ((Oh My!)). Assuming we don't have a base class called Animal, in C++, you are going to have significant work to do to because the compiler isn't going to let you do any dynamic dispatch without a common base class. The indexes for the vtables need to match up, and that can't be done between unreleated classes. You'd need to have a vtable big enough to hold the virtual methods of all classes in the system. C++ programmers rarely see this as a limitation because you have been trained to think a certain way about class design. I'm not saying its better or worse.

对于后期绑定,运行时在没有公共基类的情况下处理这个问题。通常有一个哈希表系统,用于在具有调度程序中使用的缓存系统的类中查找方法。在 C + + 中,编译器知道所有类型。在后期绑定语言中,对象本身知道它们的类型(它不是无类型的,对象本身在大多数情况下确切地知道它们是谁)。这意味着我可以有多种类型的对象数组,如果我想(狮子和老虎和熊)。你还可以实现消息转发和原型化(允许在不改变类的情况下改变每个对象的行为)以及其他各种方式,这些方式比不支持后期绑定的语言更加灵活,导致更少的代码开销。

有没有在 Android 中编程并使用 findViewById () ?您几乎总是最终强制转换结果以获得正确的类型,而强制转换基本上是对编译器撒谎,并放弃所有静态类型检查的优点,这些优点被认为是使静态语言优越的。当然,您可以使用 findTextViewById ()、 findEditTextById ()和一百万个其他类型来匹配返回类型,但是这样就把多态性抛出了窗口; 可以说是 OOP 的整个基础。后期绑定语言可能只允许根据 ID 进行索引,并将其视为哈希表,而不关心索引的类型或返回的类型。

这是另一个例子。假设您有 Lion 类,它的默认行为是在您看到它时吃掉您。在 C + + 中,如果你想要一只训练有素的狮子,你需要创建一个新的子类。原型可以让您简单地更改需要更改的特定 Lion 的一个或两个方法。阶级和类型不会改变。C + + 不能这么做。这一点很重要,因为当你从 Lion 继承了一个新的“ African SpottedLion”时,你也可以训练它。原型化不会改变类结构,因此可以扩展它。这通常是这些语言处理通常需要多重继承的问题的方式,或者可能是处理缺乏原型的多重继承。

仅供参考,Objective-C 是 C,添加了 SmallTalk 的消息传递功能,而 SmallTalk 是最初的 OOP,两者都具有上述所有特性的后期绑定功能。从微观的角度来看,后期绑定语言可能稍微慢一些,但是通常可以允许代码以一种在宏观层面上更有效的方式进行结构化,这一切都归结为首选项。