72 votes

Existe-t-il des utilisations pratiques pour le transfert dynamique vers un pointeur vide?

En C++, l' T q = dynamic_cast<T>(p); construction effectue une exécution en fonte d'un pointeur p à un autre type de pointeur T qui doit apparaître dans la hiérarchie d'héritage de la dynamique type d' *p pour réussir. C'est très bien.

Cependant, il est également possible de réaliser des dynamic_cast<void*>(p), qui sera tout simplement retourner un pointeur vers la "la plupart des dérivés de l'objet" (voir 5.2.7::7 en C++11). Je comprends que cette fonctionnalité vient probablement gratuitement dans la mise en œuvre de la dynamique de fonte, mais est-il utile dans la pratique? Après tout, son type de retour est, au mieux, void*, donc à quoi bon cela?

69voto

mitchnull Points 2950

Les dynamic_cast<void*>() peuvent en effet être utilisés pour vérifier l'identité, même s'il s'agit d'héritage multiple.

Essayez ce code:

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

Sortie:

 eq: 0, eqdc: 1
 

7voto

Steve Jessop Points 166970

Gardez à l'esprit que le C++ permet de faire des choses de l'ancien C.

Supposons que j'ai une certaine API dans laquelle je suis obligé de passer en contrebande d'un pointeur d'objet à travers le type d' void*, mais où le rappel c'est finalement passée à la volonté de connaître son type dynamique:

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

Le MAL! le code est faux, car il ne parvient pas à la présence de l'héritage multiple (et n'est pas garanti de fonctionner en l'absence, soit).

Bien sûr, l'API n'est pas très C++-style, et même le "bon" code peut aller mal si j'hérite de l' ActualType. Je ne voudrais pas prétendre que c'est une utilisation brillante de l' dynamic_cast<void*>, mais c'est une utilisation.

4voto

Praveen Points 294

Casting des pointeurs vers void* a son importance depuis le chemin du retour dans C jours. Le plus approprié est également à l'intérieur du gestionnaire de mémoire du Système d'Exploitation. Il a pour stocker tout le pointeur et l'objet de ce que vous créez. En les stockant dans le vide* ils généraliser pour stocker n'importe quel objet sur le gestionnaire de mémoire de la structure de données qui pourrait être heap/B+Tree ou simple arraylist.

Pour des raisons de simplicité prends l'exemple de la création d'un list de génériques éléments(Liste contient des éléments de complètement différentes classes). Ce serait possible seulement à l'aide de void*.

la norme dit que dynamic_cast doit retourner la valeur null pour illégaux de conversion de type standard et garantit également que tout pointeur doit être en mesure de saisir la convertir en void* et à l'arrière de avec seule exception de pointeurs de fonction.

Normal niveau de l'application pratique d'utilisation est très inférieur pour l' void* typecasting mais il est largement utilisé dans les bas niveaux/systèmes embarqués.

Normalement, vous pouvez utiliser reinterpret_cast pour les bas niveau de stuff, comme dans 8086 il est utilisé pour compenser le pointeur de la même base pour obtenir l'adresse, mais pas limité à cela.

Edit: La norme dit que vous pouvez convertir n'importe quel pointeur void* même avec dynamic_cast<> mais pas là où les états que vous ne pouvez pas convertir l' void* de retour à l'objet.

Pour la plupart d'utilisation, son sens unique de la rue mais il y a quelques inévitables d'utilisation.

Il dit simplement qu' dynamic_cast<> des besoins d'informations de type pour le reconvertir au type demandé.

Il existe de nombreuses API qui vous obligent à passer void* à un objet par exemple. java/Jni Code passe l'objet en tant que void*.
Sans info on ne peut pas faire le casting.Si vous êtes assez confiant que le type demandé est correct, vous pouvez demander au compilateur de faire l' dynmaic_cast<> avec un truc.

Regardez ce code:

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

Veuillez me corriger si ce n'est pas correct en aucune façon.

1voto

BruceAdi Points 11

il est utile de remettre le stockage dans le pool de mémoire mais de ne garder qu'un pointeur sur la classe de base. Dans ce cas, nous devrions déterminer l'adresse d'origine.

0voto

John Dibling Points 56814

Ce peut être un moyen de fournir un Pointeur Opaque par le biais de l'ABI. Opaque Pointeurs-et, plus généralement, Opaque Types de Données , sont utilisés pour transmettre des objets et d'autres ressources autour de entre la bibliothèque de code client et code de telle façon que le code du client peut être isolé à partir de la mise en œuvre de la bibliothèque. Il y a d'autres façons pour ce faire, pour être sûr, et peut-être que certains d'entre eux seraient mieux pour un cas d'utilisation particulier.

Windows fait un grand usage des Pointeurs Opaque dans son API. HANDLE est, je crois, généralement un pointeur opaque à la ressource que vous avez un HANDLE , par exemple. HANDLEs peuvent être les Objets du Noyau comme les fichiers, les objets GDI, et toutes sortes d'Objets Utilisateur de diverses sortes, qui doit être très différent dans la mise en œuvre, mais tous sont retournés comme un HANDLE à l'utilisateur.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X