una tipos programacion programa parametros funciones funcion estructuras estructura ejemplos datos con c++ oop

tipos - ¿Cuál es la diferencia entre el envío dinámico y el enlace tardío en C++?



tipos de datos en c++ (11)

Recientemente leí sobre el Despacho dinámico en Wikipedia y no pude entender la diferencia entre el envío dinámico y el enlace tardío en C ++.

Cuando se usa cada uno de los mecanismos?

La cita exacta de Wikipedia:

El envío dinámico es diferente del enlace tardío (también conocido como enlace dinámico). En el contexto de seleccionar una operación, el enlace se refiere al proceso de asociación de un nombre con una operación. El envío se refiere a elegir una implementación para la operación después de que haya decidido a qué operación se refiere un nombre. Con el envío dinámico, el nombre puede estar vinculado a una operación polimórfica en tiempo de compilación, pero la implementación no se elegirá hasta el tiempo de ejecución (así es como funciona el despacho dinámico en C ++). Sin embargo, la vinculación tardía implica el envío dinámico, ya que no puede elegir qué implementación de una operación polimórfica seleccionar hasta que haya seleccionado la operación a la que hace referencia el nombre.


Déjame darte un ejemplo de las diferencias porque NO son lo mismo. Sí, el despacho dinámico te permite elegir el método correcto cuando te refieres a un objeto por una superclase, pero esa magia es muy específica para esa jerarquía de clase, y tienes que hacer algunas declaraciones en la clase base para que funcione (métodos abstractos complete los vtables ya que el índice del método en la tabla no puede cambiar entre tipos específicos). Por lo tanto, puede llamar a métodos en Tabby y Lion and Tiger con un puntero de Cat genérico e incluso tener matrices de Cats llenas de Leones, Tigres y Tabbys. Sabe a qué índices se refieren esos métodos en vtable del objeto en tiempo de compilación (enlace estático / temprano), aunque el método se seleccione en tiempo de ejecución (envío dinámico).

¡Ahora, implementemos una matriz que contenga leones, tigres y osos! ((¡Oh mi!)). Suponiendo que no tenemos una clase base llamada Animal, en C ++, tendrá mucho trabajo que hacer, porque el compilador no le permitirá hacer un despacho dinámico sin una clase base común. Los índices de los vtables deben coincidir, y eso no se puede hacer entre clases sin traducir. Necesitaría tener un vtable lo suficientemente grande como para contener los métodos virtuales de todas las clases en el sistema. Los programadores de C ++ rara vez ven esto como una limitación porque han sido entrenados para pensar de cierta manera sobre el diseño de clase. No digo que sea mejor o peor.

Con la vinculación tardía, el tiempo de ejecución se ocupa de esto sin una clase base común. Normalmente, se utiliza un sistema de tablas hash para encontrar métodos en las clases con un sistema de caché utilizado en el despachador. Donde en C ++, el compilador conoce todos los tipos. En un lenguaje con límite de tiempo, los objetos mismos conocen su tipo (no es sin tipo, los objetos mismos saben exactamente quiénes son en la mayoría de los casos). Esto significa que puedo tener matrices de múltiples tipos de objetos si quiero (Lions and Tigers and Bears). Y puede implementar reenvío de mensajes y creación de prototipos (permite cambiar los comportamientos por objeto sin cambiar la clase) y todo tipo de cosas de manera mucho más flexible y con menos sobrecarga de código que en los idiomas que no admiten el atascamiento tardío .

¿Alguna vez programa en Android y usa findViewById ()? Casi siempre se termina arrojando el resultado para obtener el tipo correcto, y el casting básicamente le miente al compilador y renuncia a todo el bien estático de comprobación de tipo que se supone que hace que los lenguajes estáticos sean superiores. Por supuesto, podrías tener findTextViewById (), findEditTextById (), y un millón de otros para que tus tipos de retorno coincidan, pero eso está tirando polimorfismo por la ventana; posiblemente la base completa de OOP. Un lenguaje con límite de tardanza probablemente le permita simplemente indexar por una ID, y tratarlo como una tabla de hash y no importar qué tipo se indexó ni se devolvió.

Aquí hay otro ejemplo. Digamos que tienes tu clase de León y su comportamiento predeterminado es comerte cuando lo ves. En C ++, si quisieras tener un solo león "entrenado", necesitas crear una nueva subclase. Los prototipos le permiten simplemente cambiar los uno o dos métodos de ese León en particular que deben cambiarse. Su clase y tipo no cambian. C ++ no puede hacer eso. Esto es importante ya que cuando tienes un nuevo "AfricanSpottedLion" que hereda de Lion, también puedes entrenarlo. El prototipo no cambia la estructura de clases, por lo que puede expandirse. Normalmente, estos lenguajes manejan problemas que normalmente requieren herencia múltiple, o tal vez la herencia múltiple es la forma en que se maneja la falta de creación de prototipos.

Para su información, Objective-C es C con el mensaje agregado de SmallTalk agregado y SmallTalk es el OOP original, y ambos están vinculados con retraso con todas las características anteriores y más. Los lenguajes de límite tardío pueden ser un poco más lentos desde un punto de vista de nivel micro, pero a menudo pueden permitir que el código se estructure de una manera que sea más eficiente en un macro nivel, y todo se reduce a preferencia.


Dada esa definición engorrosa de Wikipedia, estaría tentado de clasificar el despacho dinámico como el enlace tardío de C ++

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

El enlace tardío en su lugar, para Wikipedia, parece significar el envío de puntero a miembro de C ++

(this->*mptr)();

donde la selección de cuál es la operación que se invoca (y no solo qué implementación) se realiza en tiempo de ejecución.

En la literatura de C ++, sin embargo, el late binding se usa normalmente para lo que Wikipedia llama despacho dinámico.


El Wikipedia sí explica la diferencia:

El envío dinámico es diferente del enlace tardío (también conocido como enlace dinámico). En el contexto de seleccionar una operación, el enlace se refiere al proceso de asociación de un nombre con una operación. El envío se refiere a elegir una implementación para la operación después de que haya decidido a qué operación se refiere un nombre.

y

Con el envío dinámico, el nombre puede estar vinculado a una operación polimórfica en tiempo de compilación, pero la implementación no se elegirá hasta el tiempo de ejecución (así es como funciona el despacho dinámico en C ++). Sin embargo, la vinculación tardía implica el envío dinámico, ya que no puede elegir qué implementación de una operación polimórfica seleccionar hasta que haya seleccionado la operación a la que hace referencia el nombre.

Pero en su mayoría son iguales en C ++, puede hacer un despacho dinámico mediante funciones virtuales y tablas virtuales.

C ++ utiliza enlace anticipado y ofrece despacho dinámico y estático. La forma predeterminada de envío es estática. Para obtener un despacho dinámico, debe declarar un método como virtual.


El despacho dinámico es lo que sucede cuando utiliza la palabra clave virtual en C ++. Así por ejemplo:

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

imprimirá 3 , porque el método se ha enviado dinámicamente . El estándar de C ++ tiene mucho cuidado de no especificar cómo exactamente sucede esto detrás de escena, pero cada compilador bajo el sol lo hace de la misma manera. Crean una tabla de punteros de función para cada tipo polimórfico (llamada la tabla virtual o vtable ), y cuando llama a un método virtual, el método "real" se busca desde el vtable y se llama a esa versión. Para que pueda obtener imágenes de algo así como este pseudocódigo:

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

De esta forma, el compilador puede estar seguro de que existe un método con una firma particular en tiempo de compilación. Sin embargo, en tiempo de ejecución, la llamada podría enviarse a través de vtable a una función diferente. Las llamadas a funciones virtuales son un poco más lentas que las llamadas no virtuales, debido al paso de indirección adicional.

Por otro lado, mi comprensión del término enlace tardío es que el puntero de función se busca por nombre en tiempo de ejecución, desde una tabla hash o algo similar. Esta es la forma en que se hacen las cosas en Python, JavaScript y (si la memoria sirve) Objective-C. Esto hace posible agregar nuevos métodos a una clase en tiempo de ejecución , lo que no se puede hacer directamente en C ++. Esto es particularmente útil para implementar cosas como mixins. Sin embargo, la desventaja es que la búsqueda en tiempo de ejecución generalmente es considerablemente más lenta que incluso una llamada virtual en C ++, y el compilador no puede realizar ninguna verificación de tipo en tiempo de compilación para los métodos recién agregados.


En C ++, ambos son lo mismo.

En C ++, hay dos tipos de enlace:

  • enlace estático - que se realiza en tiempo de compilación.
  • vinculación dinámica, que se realiza en tiempo de ejecución.

La vinculación dinámica, dado que se realiza en tiempo de ejecución, también se conoce como vinculación tardía y la vinculación estática a veces se denomina vinculación anticipada .

Mediante el enlace dinámico, C ++ admite el polimorfismo en tiempo de ejecución a través de funciones virtuales (o indicadores de función ), y mediante el uso de enlace estático, todas las demás funciones se resuelven.


En C ++, tanto dynamic dispatch como el late binding son los mismos. Básicamente, el valor de un solo objeto determina el fragmento de código invocado en tiempo de ejecución. En idiomas como C ++ y Java, el despacho dinámico es más específicamente el despacho único dinámico que funciona como se mencionó anteriormente. En este caso, dado que la vinculación se produce en tiempo de ejecución, también se denomina late binding . Los lenguajes como smalltalk permiten el despacho múltiple dinámico en el que el método de tiempo de ejecución se elige en el tiempo de ejecución en función de las identidades o valores de más de un objeto.

En C ++, realmente no tenemos enlaces finales, porque se conoce la información del tipo. Por lo tanto, en el contexto de C ++ o Java, el envío dinámico y el enlace tardío son los mismos. La unión real / totalmente tardía, creo que está en lenguajes como Python, que es una búsqueda basada en métodos en lugar de basada en tipos.


Esta question puede ayudarte.

El despacho dinámico generalmente se refiere al despacho múltiple.

Considera el siguiente ejemplo. Espero que te pueda ayudar.

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

Considera el caso de abajo.

Derived1 * d = new Derived1; Base2 * b = new Derived2; //Now which function1 will be called. d->function1(b);

Base2* function1 tomando Base2* no Derived2* . Esto se debe a la falta de despacho múltiple dinámico.

La vinculación tardía es uno de los mecanismos para implementar el envío único dinámico.


La vinculación tardía llama a un método por nombre durante el tiempo de ejecución. Realmente no tienes esto en c ++, excepto para importar métodos de una DLL.
Un ejemplo para eso sería: GetProcAddress ()

Con el envío dinámico, el compilador tiene suficiente información para llamar a la implementación correcta del método. Esto generalmente se hace creando una tabla virtual .


Supongo que el significado es que cuando tienes dos clases B, C hereda la misma clase padre A. Entonces, el puntero del padre (tipo A) puede contener cada uno de los tipos hijo. El compilador no puede saber qué tipo de letra tiene el puntero en un tiempo determinado, porque puede cambiar durante la ejecución del programa.

Hay funciones especiales para determinar qué tipo de determinado objeto en determinado tiempo. como instanceof en java, o por if(typeid(b) == typeid(A))... en c ++.


Una respuesta bastante decente a esto, de hecho, se incorpora a una pregunta sobre el enlace tardío frente a los programmers.stackexchange.com .

En resumen, el enlace tardío se refiere al lado del objeto de un eval, el envío dinámico se refiere al lado funcional. En la vinculación tardía, el tipo de variable es la variante en tiempo de ejecución. En dynamic-dispatch, la función o subrutina que se está ejecutando es la variante.

En C ++, realmente no tenemos un enlace tardío porque se conoce el tipo (no necesariamente el final de la jerarquía de herencia, pero al menos una clase base o interfaz formal). Pero tenemos despacho dinámico a través de métodos virtuales y polimorfismo.

El mejor ejemplo que puedo ofrecer para la vinculación tardía es el "objeto" sin tipo en Visual Basic. El entorno de tiempo de ejecución hace todo el trabajo pesado de enlace posterior para usted.

Dim obj - initialize object then.. obj.DoSomething()

El compilador realmente codificará el contexto de ejecución apropiado para el motor de tiempo de ejecución para realizar una búsqueda con nombre del método llamado DoSomething , y si se descubre con los parámetros que coincidan adecuadamente, realmente ejecutará la llamada subyacente. En realidad, se conoce algo sobre el tipo de objeto (hereda de IDispatch y es compatible con GetIDsOfNames() , etc.). pero en lo que respecta al lenguaje , el tipo de la variable es completamente desconocido en el momento de la compilación, y no tiene idea de si DoSomething es incluso un método para cualquier obj realidad hasta que el tiempo de ejecución llegue al punto de ejecución.

No me molestaré en descargar una interfaz virtual de C ++, ya que estoy seguro de que ya sabes cómo son. Espero que sea obvio que el lenguaje C ++ simplemente no puede hacer esto. Está fuertemente tipado. Puede (y lo hace, obviamente) realizar despacho dinámico a través de la función de método virtual polimórfico.


La vinculación se refiere al proceso de asociación de un nombre con una operación.

Lo principal aquí son los parámetros de función. Estos deciden a qué función llamar en tiempo de ejecución.

El envío se refiere a elegir una implementación para la operación después de que haya decidido a qué operación se refiere un nombre.

enviar el control a eso de acuerdo con la coincidencia de parámetros

Wikipedia

espero que esto te ayude