2009-04-27 5 views
1

Disons que j'ai une structure nommée sommet avec une méthode qui ajoute deux sommets:Quand est-il prudent d'ajouter des valeurs d'objet aux vecteurs sur le tas?

struct vertex { 
    float x, y, z; 

    // constructs the vertex with initial values 
    vertex(float ix, float iy, float iz); 

    // returns the value c = this + b 
    vertex operator+(vertex b); 
}; 

vertex::vertex(float ix, float iy, float iz){ 
    this->x = ix; this->y = iy; this->z = iz; 
} 

vertex vertex::operator+(vertex b){ 
    vertex c; 
    c.x = this->x + b.x; 
    c.y = this->y + b.y; 
    c.z = this->z + b.z; 
    return c; 
} 

Dans une autre fonction d'appel que je veux ajouter deux sommets et ajouter le résultat à une vector<vertex*>. Quand est-il prudent d'utiliser la valeur renvoyée pour ajouter au vecteur donné? Si jamais, comment pourrais-je l'implémenter?

Par exemple,

vector<vertex*> v; 
vertex a(1, 2, 3); 
vertex b(4, 5, 6); 
v.push_back(&(a + b)); 

Répondre

2

Ce n'est pas sûr, puisque vous stockez un pointeur vers une variable automatique ou temporaire, qui sera récupéré lorsque la fonction en cours se termine.

Le mélange d'objets alloués dynamiquement avec des objets alloués automatiquement constitue un risque sérieux. Parfois, la meilleure stratégie consiste à interdire complètement l'allocation automatique (par exemple, en rendant le constructeur privé et en utilisant une méthode d'usine pour créer une nouvelle instance). Vous seriez alors responsable de les éliminer à un moment donné.

Une deuxième option (pas nécessairement ce que vous voulez) est de tout faire par valeur. Avoir un vecteur de Vertex plutôt que Vertex *, et juste avoir les sommets copiés quand ils sont stockés. La façon dont votre classe est écrite, tous les champs sont des primitives, donc cela pourrait être assez bon et vous ne rencontrerez pas de problèmes sémantiques de performance ou de copie profonde.

+0

Cela clarifie un peu, mais étant donné que, quel est un moyen sûr de le faire sans renvoyer des pointeurs de la fonction opérateur? Je veux pouvoir chaîner différents opérateurs ensemble - c'est-à-dire a + b + c. – Asuah

+0

Vous pouvez toujours le faire si vous utilisez une sémantique de valeur par passe et de retour par valeur. Ce sera en sécurité. Il suffit de faire un vecteur d'objets vertex plutôt qu'un vecteur de pointeurs vers des sommets. – Uri

+0

Utiliser le pointeur intelligent est une autre option ici. –

1

Ce n'est jamais enregistré, puisque vous ajoutez un pointeur à un objet temporaire au vecteur. Cet objet temporaire sera détruit après l'exécution de la ligne, laissant le vecteur avec un pointeur invalide.

Vous avez deux possibilités. Soit vous ne stockez pas des pointeurs dans le vecteur et d'utiliser un vector<vertex> à la place, ou vous allouez explicitement une nouvelle copie de l'objet temporaire lorsque vous ajoutez:

v.push_back(new vertex(a+b)); 
1

Une autre alternative est d'insérer smart-pointeur dans un récipient std, comme boost: shared_ptr, puisque le pointeur partagé prendra soin de la gestion de la mémoire pour vous. Cependant, vous devez renvoyer un pointeur partagé de Shared_Ptr vertex :: operator + (vertex b), ce qui signifie que la valeur de retour sera toujours sur le tas.

Si vous n'êtes pas familier avec boost: shared_ptr, alors faites tout par valeur comme suggéré par les autres publications. C'est en fait une pratique standard.

1

Ceci n'est pas sûr, car les valeurs de sommet que vous ajoutez au vecteur sont et non sur le tas. Les objets vertex renvoyés sont sur la pile, de sorte que leurs emplacements mémoire peuvent être écrasés une fois que la fonction en cours se termine. Si le vecteur (ou une copie de celui-ci) persiste après la fin de la fonction en cours, il n'y a aucune garantie que ses pointeurs se référeront toujours aux objets vertex valides.

La partie la plus dangereuse de cette situation est que ces objets sommet peuvent survivre dans la mémoire longtemps après la fin de la fonction dans laquelle ils ont été créés. J'ai vu un exemple de ceci une fois, où un étudiant a rempli un vecteur avec des pointeurs pour évaluer des objets dans un très long constructeur. Le vecteur était un champ membre dans l'objet, donc il existait toujours après la fin du constructeur. Parce que le constructeur était si long, les objets de valeur que le vecteur pointait vers n'étaient pas remplacés jusqu'à mi-chemin d'une fonction différente. Regarder le contenu du vecteur dans un débogueur a créé l'illusion perplexe que les objets se corrompaient spontanément en mémoire.

Sauf si vous avez réellement besoin de stocker ces sommets comme pointeurs, la stratégie la plus sûre consiste à les stocker en tant qu'objets de valeur dans le vecteur: vector<vertex> au lieu de vector<vertex*>. Vous pourrez toujours enchaîner les différents opérateurs ensemble; en fait, il devrait être plus facile de les changer ensemble en tant qu'objets de valeur, car vous n'aurez pas constamment besoin de déréférencer les pointeurs.

Questions connexes