Question sur les vecteurs de pointeurs

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je viens vers vous pour une petite question avec les pointeurs, les vecteurs et la POO. J’ai en effet une classe Equipment qui peut elle-même contenir une liste d’équipements. En ne gardant que l’essentiel, j’ai donc :

1
2
3
4
5
6
7
8
class Equipment
{
public:
    Equipment();

private:
    QVector<Equipment> m_subequipments;
};

J’aimerais optimiser mon programme un peu et pour cela je pensais passer à des pointeurs dans ma liste plutôt que de copier les équipements. Est-ce une bonne pratique ?

Du coup j’obtiendrais la ligne suivante : QVector<Equipment*> m_subequipments;. Faut-il que je fasse également le destructeur de ma classe du coup ? Quelque chose comme :

1
2
3
4
5
6
7
Equipment::~Equipment()
{
    for (int i = 0; i < m_subequipments.size(); ++i) {
        delete m_subequipments[i];
        m_subequipments[i] = nullptr;
    }
}

Merci pour votre aide!

la plupart du temps remplacé un *T par shared_ptr<T> est une bonne idée.

Sans le reste du code on ne peux rien affirmer sur sa bonne utilisation dans ce cas, mais shared_ptr évite les problèmes des pointeurs nus

Ta classe devient donc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//obligatoir pour utiliser les smart pointers
#include <memory> 

class Equipment
{
public:
    Equipment();

private:
    QVector<shared_ptr<T> > m_subequipments;
};
+0 -0

Sans le reste du code on ne peux rien affirmer sur sa bonne utilisation dans ce cas, mais shared_ptr évite les problèmes des pointeurs nus

Justement le code est au tout début, rien n’est encore fait.

Du coup en utilisant le shared_ptr, plus besoin destructeur, le pointeur s’en occupe tout seul ?

Merci pour ton aide ! :D

+0 -0

Plus besoins de destructeur en effet.

Le modèle derrière ce pointeur est celui où plusieurs pointeurs permettent l’accès à une même donnée, sans que l’un de ceux-ci soit investi du rôle particulier consistant à être le responsable de la durée de vie de l’objet. Cette responsabilité est partagée (d’où le nom shared) entre tous les pointeurs pointant à un moment donné sur l’objet.

En fait, quand un shared pointeur est détruit, il vérifie s’il n’était pas le dernier à pointer sur son objet. Si c’est le cas, il détruit l’objet, puisqu’il est sur le point de devenir inaccessible. Afin de connaître cette information, un compteur de référence est associé à l’objet, à chaque fois qu’un nouveau pointeur pointe sur l’objet, le compteur de référence est incrémenté. A chaque fois que le pointeur arrête de pointer sur cet objet (parce qu’il est détruit, ou s’apprête à pointer sur un autre), le compteur est décrémenté. Si le compteur atteint 0, il est temps de détruire l’objet.

tiré d’ici
+0 -0

J’ai personnellement presque jamais vu un moment où utiliser un std::shared_ptr était un bon choix.

La première question à se poser, c’est en quoi utiliser un vecteur de pointeur va optimiser ton programme? Sans plus de contexte, j’aurais tendance à dire que ça va plutôt empirer les choses puisque tes accès mémoire seront bien moins prédictibles, ce qui augmentera certainement les chances de défaut de cache. Ça ne va clairement pas optimiser les choses.

Tu parles d’éviter des copies, j’en conclue donc que ton objet Equipement est probablement d’une taille significative et que tes instances sont baladés d’un conteneur à un autre. Si c’est bien le cas, utiliser des pointeurs est en effet une bonne idée.

Comme l’a fait remarqué d3m0t3p, utiliser des pointeurs intelligents est plus pratique. En revanche, à moins que tu veuilles qu’un équipement puisse être contenu dans plusieurs conteneurs à la fois, utiliser std::unique_ptr est plus performant et sémantiquement plus correct.

Ça s’utilise plus ou moins comme un std::shared_ptr, en un peu plus compliqué. Globalement, lorsque tu veux bouger un std::unique_ptr d’un endroit à un autre, tu ne peux pas simplement le copier et supprimer l’ancienne version, tu dois bouger le pointeur avec std::move:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Initialisation du premier conteneur (origine)
std::vector<std::unique_ptr<int>> vecteur1;
vecteur1.emplace_back(new int(1));
vecteur1.emplace_back(new int(2));

// Initialisation du deuxième conteneur (destination)
std::vector<std::unique_ptr<int>> vecteur2;

// Déplacement du premier élément de vecteur1 dans vecteur2
vecteur2.emplace_back(std::move(vecteur1[0]));
// vecteur2 contiens maintenant 1 et le premier élément de vecteur1 est un pointeur null.
// Il faut donc maintenant le retirer:
vecteur1.erase(vecteur1.begin());

Si avoir des pointeurs vide dans ton équipement ne te dérange pas (par exemple, si ton équipement a une taille fixe où les pointeurs null sont des emplacements vide), tu peux te contenter d’utiliser std::swap pour échanger deux éléments:

1
2
3
4
5
6
7
8
9
std::array<int, 5> petit_array;
std::array<int, 20> grand_array;

// On met quelques éléments dans petit_array
petit_array[0] = std::make_unique<int>(42);
petit_array[2] = std::make_unique<int>(1000);

// On déplace le premier élément de petit_array à la fin de grand_array
std::swap(petit_array[0], grand_array[19]);
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte