c++ - pointer - unique_ptr reset




Modo corretto per creare unique_ptr che contiene una matrice allocata (4)

Sembra una bibita, ti spiegherò cosa intendo

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;
}

Obiettivo del post è che ci sono piccole cose nascoste nascoste che devi capire. La creazione di una matrice di oggetti è uguale alla matrice di oggetti di unique_ptr. Farà la differenza solo quando lo passerai nell'argomento. Anche la creazione di array di puntatori di oggetti di unique_ptr non è molto utile. Quindi solo sotto due è necessario utilizzare nella maggior parte degli scenari.

unique_ptr<Object> obj;
//and 
unique_ptr<unique_ptr<Object>[]>= make_unique<unique_ptr<Object>[]>(20);

Qual è il modo corretto di creare un unique_ptr che contiene una matrice allocata nell'archivio gratuito? Visual Studio 2013 supporta questo di default, ma quando uso gcc versione 4.8.1 su Ubuntu ottengo perdite di memoria e comportamento indefinito.

Il problema può essere riprodotto con questo codice:

#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 darà questo risultato:

==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)

Un modo molto probabilmente migliore sarebbe usare invece std::vector<unsigned char>

#include <vector>
#include <string>

using namespace std;

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

Il vantaggio è che questo è molto meno soggetto a errori e consente di accedere a tutti i tipi di funzionalità come iterazione semplice, inserimento, riallocazione automatica quando la capacità è stata raggiunta.

C'è un avvertimento: se stai spostando i tuoi dati molto, un std::vector costa un po 'di più perché tiene traccia delle dimensioni e della capacità, piuttosto che solo l'inizio dei dati.

Nota: il tuo memset non fa nulla perché lo chiami con un argomento di conteggio zero.


Utilizzando la specializzazione T[] :

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

Nota che, in un mondo ideale, non dovresti usare esplicitamente new per istanziare un unique_ptr , evitando una potenziale trappola di sicurezza. A tal fine, C ++ 14 fornisce il modello di funzione std::make_unique . Guarda questo eccellente GOTW per maggiori dettagli. La sintassi è:

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

unsigned int size=16000;
std::unique_ptr<unsigned char[], std::default_delete<unsigned char[]>> pData(new unsigned char[size]);