web-dev-qa-db-fra.com

Méthode appropriée pour créer unique_ptr contenant un tableau alloué

Quelle est la manière appropriée de créer un unique_ptr contenant un tableau alloué sur le magasin gratuit? Visual studio 2013 prend cela en charge par défaut, mais lorsque j'utilise la version 4.8.1 de gcc sur Ubuntu, des fuites de mémoire et un comportement indéfini se produisent.

Le problème peut être reproduit avec ce code: 

#include <memory>
#include <string.h>

using namespace std;

int main()
{
    unique_ptr<unsigned char> testData(new unsigned char[16000]());

    memset(testData.get(),0x12,0);

    return 0;
}

Valgrind donnera cette sortie:

==3894== 1 errors in context 1 of 1:
==3894== Mismatched free() / delete / delete []
==3894==    at 0x4C2BADC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==3894==    by 0x400AEF: std::default_delete<unsigned char>::operator()(unsigned char*) const (unique_ptr.h:67)
==3894==    by 0x4009D0: std::unique_ptr<unsigned char, std::default_delete<unsigned char> >::~unique_ptr() (unique_ptr.h:184)
==3894==    by 0x4007A9: main (test.cpp:19)
==3894==  Address 0x5a1a040 is 0 bytes inside a block of size 16,000 alloc'd
==3894==    at 0x4C2AFE7: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==3894==    by 0x40075F: main (test.cpp:15)
42
lauw

Utilisation de la spécialisation T[]:

std::unique_ptr<unsigned char[]> testData(new unsigned char[16000]());

Notez que, dans un monde idéal, vous n’auriez pas à utiliser explicitement new pour instancier un unique_ptr, évitant ainsi un écueil potentiel en matière de sécurité des exceptions. À cette fin, C++ 14 vous fournit le modèle de fonction std::make_unique. Voir cet excellent GOTW pour plus de détails. La syntaxe est la suivante:

auto testData = std::make_unique<unsigned char[]>(16000);
61
juanchopanza

Utilisez la version du tableau:

auto testData = std::unique_ptr<unsigned char[]>{ new unsigned char[16000] };

Ou avec c ++ 14, une meilleure forme (VS2013 l’a déjà):

auto testData = std::make_unique<unsigned char[]>( 16000 );
25
galop1n

Une meilleure solution serait d'utiliser std::vector<unsigned char> à la place.

#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<unsigned char> testData(0x12, 0); // replaces your memset
    // bla    
}

L'avantage est que cela est beaucoup moins sujet aux erreurs et vous donne accès à toutes sortes de fonctionnalités telles que l'itération facile, l'insertion, la réallocation automatique lorsque la capacité est atteinte. 

Il y a une mise en garde: si vous déplacez beaucoup vos données, un std::vector coûte un peu plus cher, car il garde également une trace de la taille et de la capacité, plutôt que du début des données.

Remarque: votre memset ne fait rien car vous l’appelez avec un argument zéro.

14
TemplateRex

On dirait une gaffe, je vais expliquer ce que je veux dire

class Object {
private :
    static int count;
public :
    Object() {
        cout << "Object Initialized " << endl;
        count++;
    }
    ~Object() {
        cout << "Object destroyed " << endl;
    }
    int print()
    {
        cout << "Printing" << endl;
        return count;
    }
};

int Object::count = 0;

int main(int argc,char** argv)
{
    // This will create a pointer of Object
    unique_ptr<Object> up2 = make_unique<Object>();  
    up2->print();
    // This will create a pointer to array of Objects, The below two are same. 
    unique_ptr<Object[]> up1 = std::make_unique<Object[]>(30);
    Object obj[30];
    cout << up1.get()[8].print();
    cout << obj[8].print();

    // this will create a array of pointers to obj. 
        unique_ptr<Object*[]> up= std::make_unique<Object*[]>(30);
        up.get()[5] = new Object();
        unique_ptr<Object> mk = make_unique<Object>(*up.get()[5]);
        cout << up.get()[5]->print();

        unique_ptr<unique_ptr<Object>[]> up3 =  std::make_unique<unique_ptr<Object>[]>(20);
        up3.get()[5] = make_unique<Object>();

    return 0;
}

L’objectif de la publication est qu’il y ait des petites choses subtiles que vous devez comprendre. La création d'un tableau d'objets est identique à celle d'un tableau unique_ptr. Cela ne fera la différence que lorsque vous passerez l'argument. La création d'un tableau de pointeurs d'objet de unique_ptr n'est également pas très utile. Vous ne devez donc utiliser que moins de deux dans la plupart des scénarios.

unique_ptr<Object> obj;
//and 
unique_ptr<unique_ptr<Object>[]>= make_unique<unique_ptr<Object>[]>(20);
0
Raghavendar Reddy
unsigned int size=16000;
std::unique_ptr<unsigned char[], std::default_delete<unsigned char[]>> pData(new unsigned char[size]);
0
Competent Bit