2010-06-07 13 views
1

i un « fournisseur de données » qui stocke sa sortie dans une struct d'un certain type, par exempleC++ Comment copier en profondeur une structure avec un type de données inconnu?

struct DATA_TYPE1{ 
    std::string data_string; 
}; 

alors ce struct doit être coulé dans un type général, je pensais void * ou char *, car l'objet "intermédiaire" qui le copie et le stocke dans son arbre binaire devrait pouvoir stocker de nombreux types différents de telles données de structure.

struct BINARY_TREE_ENTRY{ 
    void * DATA; 
    struct BINARY_TREE_ENTRY * next; 
}; 

ce vide * est alors repris plus tard par un autre objet qui jette le vide * retour dans la (struct DATA_TYPE1 *) pour obtenir les données d'origine. donc l'expéditeur et le récepteur connaissent le type de données DATA_TYPE1 mais pas l'objet de copie entre eux.

mais comment l'objet intermidiate copie en profondeur le contenu de les différents struct, quand il ne connaît pas le type de données, que void * et il n'a pas de méthode pour copier le contenu réel; Dynamic_cast ne fonctionne pas pour void *;

l'objet « intermédiaire » devrait faire quelque chose comme:

void store_data(void * CASTED_DATA_STRUCT){ 
    void * DATA_COPY = create_a_deepcopy_of(CASTED_DATA_STRUCT); 
    push_into_bintree(DATA_COPY); 
} 

une solution simple serait que l'objet émetteur ne supprimer le struct données envoyées, jusqu'à l'objet de réception a obtenu, mais le envoyer des objets sont créés et supprimés de façon dynamique, avant que le récepteur a reçu les données de l'objet intermédiaire, pour la communication asynchrone, par conséquent je veux copier il.

au lieu de le convertir en void * i a également essayé de conversion à un pointeur superclasse dont la copie intermédiaire objet connaît, et qui est héritée par tous les différents types de données des structs:

struct DATA_BASE_OBJECT{ 
public: 
    DATA_BASE_OBJECT(){} 
DATA_BASE_OBJECT(DATA_BASE_OBJECT * old_ptr){ 
     std::cout << "this should be automatically overridden!" << std::endl; 
    } 
    virtual ~DATA_BASE_OBJECT(){} 
}; 

struct DATA_TYPE1 : public DATA_BASE_OBJECT { 
public: 
    string str; 
    DATA_TYPE1(){} 
    ~DATA_TYPE1(){} 
    DATA_TYPE1(DATA_TYPE1 * old_ptr){ 
     str = old_ptr->str; 
    } 
}; 

et l'entrée de l'arbre binaire correspondant serait alors:

struct BINARY_TREE_ENTRY{ 
     struct DATA_BASE_OBJECT * DATA; 
     struct BINARY_TREE_ENTRY * next; 
    }; 

et de copier alors le type de données inconnu, je l'ai essayé dans la classe tha t obtient juste le type de données inconnu comme DATA_BASE_OBJECT struct * (avant qu'il ne soit le vide *):

void * copy_data(DATA_BASE_OBJECT * data_that_i_get_in_the_sub_struct){ 
    struct DATA_BASE_OBJECT * copy_sub = new DATA_BASE_OBJECT(data_that_i_get_in_the_sub_struct); 
    push_into_bintree(copy_sub); 
} 

i alors ajouté un constructeur de copie au DATA_BASE_OBJECT, mais si le DATA_TYPE1 struct est d'abord casté à un DATA_BASE_OBJECT puis copié , le sous-objet inclus DATA_TYPE1 n'est pas copié également.

i alors pensé que de trouver la taille de l'objet réel à copier et puis juste memcopy, mais les octets ne sont pas stockés dans une ligne et comment puis-je trouver la taille réelle en mémoire du struct DATA_TYPE1 qui contient une chaîne std :: string?Quelles autres méthodes C++ sont disponibles pour copier en profondeur un type de données inconnu (et peut-être obtenir les informations de type de données d'une autre manière pendant l'exécution )?

Répondre

9

Si vous avez un void *, il n'y a aucun moyen d'en extraire des informations de type. C'est pourquoi void * sont très, très rarement utilisés dans les programmes C++ (je ne me souviens pas honnêtement la dernière fois que j'en ai utilisé un) - votre approche ici est complètement erronée. Si vous voulez des conteneurs génériques dont le type est connu au moment de la compilation, utilisez des modèles. Si vous voulez des conteneurs dont le type varie au moment de l'exécution, dérivez d'une classe de base et utilisez des conteneurs de pointeurs de classe de base. Et n'écrivez pas vos propres conteneurs (sauf peut-être en tant qu'exercices d'apprentissage) - C++ a un arbre binaire parfaitement bien implémenté comme std :: set et std :: map. Et enfin, n'utilisez pas toutes les majuscules pour les noms de types C++.

+0

+1 pour m'avoir battu de 19 secondes. – Cam

+0

Pour souligner ce que Neil a ajouté à propos de la norme: Une bonne utilisation de la norme std est essentielle si vous voulez coder correctement en C++. Il semble que vous codiez d'une manière très semblable à C (par exemple votre utilisation de pointeurs vides), ce qui n'est généralement pas accepté comme bon C++. Vous devriez regarder dans la STL et aussi regarder dans les modèles. – Cam

+2

+1 pour décourager TOUS LES CAPS. Mes yeux! :-D – DevSolar

0

Les pointeurs de vide ne sont pas une bonne solution ici car ils ne garantissent pas la sécurité de type à la compiletime.

Je suggère d'utiliser des modèles. Cela vous permettra de gérer encore différents types de données en utilisant la même classe/fonctions, et garantira également la sécurité des types bien mieux que les pointeurs de vide.

Edit: Pour clarifier davantage pourquoi des pointeurs vides existent même: pointeurs vides ont été utilisés pour ce genre de chose en C. Cependant, alors que vous pouvez toujours les utiliser en C++, il est généralement découragé parce qu'il existe de meilleures solutions.

En outre, vous mentionnez deepcopying. Tous les types à utiliser avec la base de données devrait soit avoir à mettre en œuvre la fonction deepCopy (ce qui est une approche de style POO), ou vous pouvez simplement se rappeler de surcharger l'opérateur d'affectation pour tous les types que vous utilisez avec votre data_base :)

0

mais comment l'objet intermidiate peut-il copier en profondeur le contenu des différentes structures, lorsqu'il ne connaît pas le type de données, seulement void * et qu'il n'a pas de méthode pour copier le contenu réel; dynamic_cast ne fonctionne pas pour void *;

Vous ne pouvez tout simplement pas faire cela. Un bloc void * représentant certaines données contient forcément plusieurs pointeurs (std :: string alloue dynamiquement de la mémoire, par exemple). Et vous ne saurez pas exactement où ils sont stockés, il n'y aura donc aucun moyen de copier en profondeur les données sans causer de dégâts quelque part.

dynamique_cast ne fonctionne pas pour void *;

Vous pouvez essayer de transformer void * en un type de base, puis en dynamic_cast en ce que vous voulez. Cependant, je ne peux pas garantir qu'il sera sûr d'utiliser cela avec un objet créé par héritage multiple, par exemple. Il sera plus sûr d'utiliser une classe abstraite pour échanger des données, au lieu des pointeurs void *.

0

Quelles autres c méthodes de ++ sont disponibles pour deepcopy un type de données inconnu (et peut-être obtenir les informations de type de données en quelque sorte d'autre lors de l'exécution)

Qu'est-ce que vous cherchez ici est sérialisation: La possibilité de placer Divers objets dans un flux binaire, déplacez le flux - et peut-être le stocker, puis récupérer une copie du contenu original hors du flux.

Vous pouvez implémenter cela vous-même, ou vous pouvez baser votre implémentation sur des bibliothèques existantes.

Une option que j'ai utilisée est boost :: serialization. Vous devez essentiellement implémenter une méthode serialize dans toute votre hiérarchie de classe, ou une méthode save et une méthode load dans toute votre hiérarchie de classes.

Le code est assez simple. Voir here pour le tutoriel.

0

merci pour les réponses rapides.

Je suis en quelque sorte trop paresseux pour réécrire le code à l'avenir à chaque fois dans chaque classe quand un nouveau type de données est ajouté. Pour moi maintenant je pourrais le faire avec des gabarits pour chaque type de données utilisé, mais je me demande vraiment du point de vue du développement de code si n'est pas une chose plus simple en c + + . vous avez raison de dire que je viens de c et je vais me comporter lentement lors de la programmation en C++.

i essayé cette approche avec l'héritage d'une classe de base, ce qui est déjà une restriction pour le codage, parce que chaque nouveau type de données doit être codé pour inherite la struct de base, mais cela ne fonctionne pas, parce que l'appel le constructeur de la structure de base, qui est la seule chose que le "copy-object" sait (parce qu'il ne sait rien sur les types de structure dérivés), peut faire, ne fonctionne pas.

le constructeur de copie de la structure dérivée n'est pas appelé, ou est-ce que je fais quelque chose de mal ici? Je l'ai essayé avec l'opérateur d'affectation, mais il ne fait pas une copie et dès que les données d'origine sont supprimées, c'est un pointeur qui pend, je suppose.

Actuellement, il fonctionne comme ceci

  1. « l'objet d'envoi » crée un DATA_TYPE1 struct * avec de nouveaux DATA_TYPE1 ...
  2. et remplit avec les données
  3. et jette à void *
  4. et indique la « copie-objet » sur ce pointeur
  5. alors le « objet d'envoi » est supprimé, le contenu du * vide est encore en vie
  6. la « copie-objet » stocke juste le vide *
  7. le « objet réception » obtient ce vide * plus tard
  8. et jette de nouveau à la DATA_TYPE1 struct * et utilise ses données
  9. et enfin pour effet de supprimer les données

donc pas la copie se fait ici remettre un peu plus le pointeur les données d'origine d'un objet à l'autre.

merci pour les conseils sur la sérialisation je pensais à ce sujet, mais au moment où je veux me tourner avec pur C++ à résoudre ce problème.

+0

Les gens implémentent normalement une méthode "clone" qui imitera un constructeur de copie virtuelle, où "clone" retournera simplement un nouveau T (* this) ;. – Puppy