web-dev-qa-db-fra.com

Tableau C ++ de pointeurs: supprimer ou supprimer []?

Cosider le code suivant:

class Foo
{
    Monster* monsters[6];

    Foo()
    {
        for (int i = 0; i < 6; i++)
        {
            monsters[i] = new Monster();
        }
    }

    virtual ~Foo();
}

Quel est le bon destructeur?

cette:

Foo::~Foo()
{
    delete [] monsters;
}

ou ca:

Foo::~Foo()
{
    for (int i = 0; i < 6; i++)
    {
        delete monsters[i];
    }
}

J'ai actuellement le constructeur le plus élevé et tout fonctionne bien, mais bien sûr, je ne peux pas voir si cela fuit ...

Personnellement, je pense que la deuxième version est beaucoup plus logique compte tenu de ce que je fais. Quoi qu'il en soit, quelle est la "bonne" façon de procéder?

39
Jasper

delete[] monsters;

Est incorrect car monsters n'est pas un pointeur vers un tableau alloué dynamiquement, il est un tableau de pointeurs. En tant que membre de classe, il sera détruit automatiquement lorsque l'instance de classe sera détruite.

Votre autre implémentation est la bonne, car les pointeurs du tableau pointent vers des objets Monster alloués dynamiquement.

Notez qu'avec votre stratégie d'allocation de mémoire actuelle, vous souhaiterez probablement déclarer votre propre constructeur de copie et votre opérateur d'affectation de copie afin que la copie non intentionnelle ne provoque pas de double suppression. (Si vous souhaitez empêcher la copie, vous pouvez les déclarer privés et ne pas les implémenter.)

44
CB Bailey

Pour new, vous devez utiliser delete. Pour new[] utilisation delete[]. Votre deuxième variante est correcte.

37

Le second est correct dans les circonstances (enfin, le moins mal, en tout cas).

Edit: "le moins mal", comme dans le code d'origine ne montre aucune bonne raison d'utiliser new ou delete en premier lieu, donc vous devriez probablement simplement utiliser:

std::vector<Monster> monsters;

Le résultat sera un code plus simple et une séparation plus nette des responsabilités.

11
Jerry Coffin

Pour simplifier l'answare, regardons le code suivant:

#include "stdafx.h"
#include <iostream>
using namespace std;

class A
{
private:
    int m_id;
    static int count;
public:
    A() {count++; m_id = count;}
    A(int id) { m_id = id; }
    ~A() {cout<< "Destructor A "   <<m_id<<endl; }
};

int A::count = 0;

void f1()
{   
    A* arr = new A[10];
    //delete operate only one constructor, and crash!
    delete arr;
    //delete[] arr;
}

int main()
{
    f1();
    system("PAUSE");
    return 0;
}

La sortie est: Destructor A 1 et puis il plante (Expression: _BLOCK_TYPE_IS_VALID (phead-nBlockUse)).

Nous devons utiliser: delete [] arr; parce que c'est supprimer tout le tableau et pas seulement une cellule!

essayez d'utiliser delete [] arr; la sortie est: Destructeur A 10 Destructeur A 9 Destructeur A 8 Destructeur A 7 Destructeur A 6 Destructeur A 5 Destructeur A 4 Destructeur A 3 Destructeur A 2 Destructeur A 1

Le même principe s'applique à un ensemble de pointeurs:

void f2()
{
    A** arr = new A*[10];
    for(int i = 0; i < 10; i++)
    {
        arr[i] = new A(i);
    }
    for(int i = 0; i < 10; i++)
    {
        delete arr[i];//delete the A object allocations.
    }

    delete[] arr;//delete the array of pointers
}

si nous utilisons delete arr au lieu de delete [] arr. cela ne supprimera pas tous les pointeurs du tableau => fuite de mémoire des objets pointeurs!

11
shai vashdi

delete[] monsters est définitivement faux. Mon débogueur de tas affiche la sortie suivante:

allocated non-array memory at 0x3e38f0 (20 bytes)
allocated non-array memory at 0x3e3920 (20 bytes)
allocated non-array memory at 0x3e3950 (20 bytes)
allocated non-array memory at 0x3e3980 (20 bytes)
allocated non-array memory at 0x3e39b0 (20 bytes)
allocated non-array memory at 0x3e39e0 (20 bytes)
releasing     array memory at 0x22ff38

Comme vous pouvez le voir, vous essayez de libérer avec la mauvaise forme de suppression (non-tableau ou tableau), et le pointeur 0x22ff38 n'a jamais été renvoyé par un appel à new. La deuxième version affiche la sortie correcte:

[allocations omitted for brevity]
releasing non-array memory at 0x3e38f0
releasing non-array memory at 0x3e3920
releasing non-array memory at 0x3e3950
releasing non-array memory at 0x3e3980
releasing non-array memory at 0x3e39b0
releasing non-array memory at 0x3e39e0

Quoi qu'il en soit, je préfère une conception où l'implémentation manuelle du destructeur n'est pas nécessaire pour commencer.

#include <array>
#include <memory>

class Foo
{
    std::array<std::shared_ptr<Monster>, 6> monsters;

    Foo()
    {
        for (int i = 0; i < 6; ++i)
        {
            monsters[i].reset(new Monster());
        }
    }

    virtual ~Foo()
    {
        // nothing to do manually
    }
};
6
fredoverflow

Votre deuxième exemple est correct; vous n'avez pas besoin de supprimer le tableau monsters lui-même, juste les objets individuels que vous avez créés.

3
Carl Norum

Il serait logique que votre code soit comme ceci:

#include <iostream>

using namespace std;

class Monster
{
public:
        Monster() { cout << "Monster!" << endl; }
        virtual ~Monster() { cout << "Monster Died" << endl; }
};

int main(int argc, const char* argv[])
{
        Monster *mon = new Monster[6];

        delete [] mon;

        return 0;
}
1
Daniel

Vous supprimez chaque pointeur individuellement, puis vous supprimez l'ensemble du tableau. Assurez-vous d'avoir défini un destructeur approprié pour les classes stockées dans le tableau, sinon vous ne pouvez pas être sûr que les objets sont correctement nettoyés. Assurez-vous que tous vos destructeurs sont virtuels afin qu'ils se comportent correctement lorsqu'ils sont utilisés avec l'héritage.

0
Eric H