uso resueltos que memoria funcion ejercicios dinamica arreglos c++ dynamic-cast

c++ - resueltos - que es calloc en c



¿Hay usos prácticos para fundición dinámica para anular el puntero? (7)

En C ++, el T q = dynamic_cast<T>(p); construcción realiza un lanzamiento en tiempo de ejecución de un puntero p a otro puntero tipo T que debe aparecer en la jerarquía de herencia del tipo dinámico de *p para tener éxito. Eso está bien y bien.

Sin embargo, también es posible realizar dynamic_cast<void*>(p) , que simplemente devolverá un puntero al "objeto más derivado" (ver 5.2.7 :: 7 en C ++ 11). Entiendo que esta característica probablemente salga gratis en la implementación del elenco dinámico, pero ¿es útil en la práctica? Después de todo, su tipo de devolución es, en el mejor de los casos, void* , ¿de qué sirve esto?


No hagas eso en casa

struct Base { virtual ~Base (); }; struct D : Base {}; Base *create () { D *p = new D; return p; } void *destroy1 (Base *b) { void *p = dynamic_cast<void*> (b); b->~Base (); return p; } void destroy2 (void *p) { operator delete (p); } int i = (destroy2 (destroy1 (create ())), i);

Advertencia : Esto no funcionará si D se define como:

struct D : Base { void* operator new (size_t); void operator delete (void*); };

y no hay forma de hacerlo funcionar.


Ampliando la respuesta de @ BruceAdi e inspirada por esta discusión , aquí hay una situación polimórfica que puede requerir un ajuste del puntero. Supongamos que tenemos esta configuración tipo fábrica:

struct Base { virtual ~Base() = default; /* ... */ }; struct Derived : Base { /* ... */ }; template <typename ...Args> Base * Factory(Args &&... args) { return ::new Derived(std::forward<Args>(args)...); } template <typename ...Args> Base * InplaceFactory(void * location, Args &&... args) { return ::new (location) Derived(std::forward<Args>(args)...); }

Ahora podría decir:

Base * p = Factory();

Pero, ¿cómo podría limpiar esto manualmente? Necesito la dirección de memoria real para llamar a ::operator delete :

void * addr = dynamic_cast<void*>(p); p->~Base(); // OK thanks to virtual destructor // ::operator delete(p); // Error, wrong address! ::operator delete(addr); // OK

O podría volver a usar la memoria:

void * addr = dynamic_cast<void*>(p); p->~Base(); p = InplaceFactory(addr, "some", "arguments"); delete p; // OK now


Esta podría ser una forma de proporcionar un puntero opaco a través de un ABI. Los punteros opacos y, más generalmente, los tipos de datos opacos se utilizan para pasar objetos y otros recursos entre el código de la biblioteca y el código del cliente de tal manera que el código del cliente se puede aislar de los detalles de implementación de la biblioteca. Hay other ways de lograr esto, sin duda, y tal vez algunos de ellos serían mejores para un caso de uso particular.

Windows usa mucho Opaque Pointers en su API. HANDLE es, en mi opinión, generalmente un puntero opaco al recurso real al que tienes HANDLE , por ejemplo. HANDLE pueden ser objetos Kernel como archivos, objetos GDI y todo tipo de objetos de usuario de varios tipos, todos los cuales deben ser muy diferentes en la implementación, pero todos se devuelven como una HANDLE al usuario.

#include <iostream> #include <string> #include <iomanip> using namespace std; /*** LIBRARY.H ***/ namespace lib { typedef void* MYHANDLE; void ShowObject(MYHANDLE h); MYHANDLE CreateObject(); void DestroyObject(MYHANDLE); }; /*** CLIENT CODE ***/ int main() { for( int i = 0; i < 25; ++i ) { cout << "[" << setw(2) << i << "] :"; lib::MYHANDLE h = lib::CreateObject(); lib::ShowObject(h); lib::DestroyObject(h); cout << "/n"; } } /*** LIBRARY.CPP ***/ namespace impl { class Base { public: virtual ~Base() { cout << "[~Base]"; } }; class Foo : public Base { public: virtual ~Foo() { cout << "[~Foo]"; } }; class Bar : public Base { public: virtual ~Bar() { cout << "[~Bar]"; } }; }; lib::MYHANDLE lib::CreateObject() { static bool init = false; if( !init ) { srand((unsigned)time(0)); init = true; } if( rand() % 2 ) return static_cast<impl::Base*>(new impl::Foo); else return static_cast<impl::Base*>(new impl::Bar); } void lib::DestroyObject(lib::MYHANDLE h) { delete static_cast<impl::Base*>(h); } void lib::ShowObject(lib::MYHANDLE h) { impl::Foo* foo = dynamic_cast<impl::Foo*>(static_cast<impl::Base*>(h)); impl::Bar* bar = dynamic_cast<impl::Bar*>(static_cast<impl::Base*>(h)); if( foo ) cout << "FOO"; if( bar ) cout << "BAR"; }


Lanzar punteros al void* tiene su importancia desde hace mucho tiempo en C días. El lugar más adecuado está dentro del administrador de memoria del sistema operativo. Tiene que almacenar todo el puntero y el objeto de lo que creas. Al almacenarlo en vacío * lo generalizan para almacenar cualquier objeto en la estructura de datos del administrador de memoria que podría ser heap/B+Tree o simple arraylist .

Para simplificar tome un ejemplo de cómo crear una list de elementos genéricos (la lista contiene elementos de clases completamente diferentes). Eso sería posible solo usando void* .

el estándar dice que dynamic_cast debe devolver nulo para la conversión de tipos ilegales y el estándar también garantiza que cualquier puntero debería ser capaz de escribir cast it para void * y atrasado con solo la excepción de los punteros de función.

El uso práctico del nivel de aplicación normal es muy inferior para el encasillado de void* pero se usa ampliamente en sistemas de bajo nivel / integrados.

Normalmente, desearía usar reinterpret_cast para cosas de bajo nivel, como en 8086 se usa para desplazar el puntero de la misma base para obtener la dirección, pero no se limita a esto.

Editar: El estándar dice que puedes convertir cualquier puntero a void* incluso con dynamic_cast<> pero en ningún lado indica que no puedes convertir el void* nuevo en el objeto.

Para la mayoría del uso, es una calle de sentido único, pero hay un uso inevitable.

Simplemente dice que dynamic_cast<> necesita información de tipo para convertirlo de nuevo al tipo solicitado.

Hay muchas API que requieren pasar el void* a algún objeto, por ejemplo. java / Jni Code pasa el objeto como void* .
Sin información tipo, no puedes hacer el casting. Si tiene la confianza de que el tipo solicitado es correcto , puede pedirle al compilador que haga el dynmaic_cast<> con un truco.

Mira este código:

class Base_Class {public : virtual void dummy() { cout<<"Base/n";} }; class Derived_Class: public Base_Class { int a; public: void dummy() { cout<<"Derived/n";} }; class MostDerivedObject : public Derived_Class {int b; public: void dummy() { cout<<"Most/n";} }; class AnotherMostDerivedObject : public Derived_Class {int c; public: void dummy() { cout<<"AnotherMost/n";} }; int main () { try { Base_Class * ptr_a = new Derived_Class; Base_Class * ptr_b = new MostDerivedObject; Derived_Class * ptr_c,*ptr_d; ptr_c = dynamic_cast< Derived_Class *>(ptr_a); ptr_d = dynamic_cast< Derived_Class *>(ptr_b); void* testDerived = dynamic_cast<void*>(ptr_c); void* testMost = dynamic_cast<void*>(ptr_d); Base_Class* tptrDerived = dynamic_cast<Derived_Class*>(static_cast<Base_Class*>(testDerived)); tptrDerived->dummy(); Base_Class* tptrMost = dynamic_cast<Derived_Class*>(static_cast<Base_Class*>(testMost)); tptrMost->dummy(); //tptrMost = dynamic_cast<AnotherMostDerivedObject*>(static_cast<Base_Class*>(testMost)); //tptrMost->dummy(); //fails } catch (exception& my_ex) {cout << "Exception: " << my_ex.what();} system("pause"); return 0; }

Por favor corrígeme si esto no es correcto de ninguna manera.


Tenga en cuenta que C ++ le permite hacer las cosas de la vieja manera C.

Supongamos que tengo alguna API en la que me veo obligado a pasar de contrabando un puntero de objeto a través del tipo void* , pero donde la devolución de llamada finalmente se pasa a conocerá su tipo dinámico:

struct BaseClass { typedef void(*callback_type)(void*); virtual callback_type get_callback(void) = 0; virtual ~BaseClass() {} }; struct ActualType: BaseClass { callback_type get_callback(void) { return my_callback; } static void my_callback(void *p) { ActualType *self = static_cast<ActualType*>(p); ... } }; void register_callback(BaseClass *p) { // service.register_listener(p->get_callback(), p); // WRONG! service.register_listener(p->get_callback(), dynamic_cast<void*>(p)); }

¡Lo malo! el código es incorrecto porque falla en presencia de herencia múltiple (y tampoco se garantiza que funcione en ausencia).

Por supuesto, la API no es muy estilo C ++, e incluso el código "correcto" puede salir mal si ActualType de ActualType . Así que no afirmaría que este es un uso brillante de dynamic_cast<void*> , pero es un uso.


es útil cuando volvemos a poner el almacenamiento en el grupo de memoria, pero solo mantenemos un puntero a la clase base. En este caso deberíamos averiguar la dirección original.


dynamic_cast<void*>() se puede usar para verificar la identidad, incluso si se trata de herencia múltiple.

Prueba este código:

#include <iostream> class B { public: virtual ~B() {} }; class D1 : public B { }; class D2 : public B { }; class DD : public D1, public D2 { }; namespace { bool eq(B* b1, B* b2) { return b1 == b2; } bool eqdc(B* b1, B *b2) { return dynamic_cast<void*>(b1) == dynamic_cast<void*>(b2); } }; int main() { DD *dd = new DD(); D1 *d1 = dynamic_cast<D1*>(dd); D2 *d2 = dynamic_cast<D2*>(dd); std::cout << "eq: " << eq(d1, d2) << ", eqdc: " << eqdc(d1, d2) << "/n"; return 0; }

Salida:

eq: 0, eqdc: 1