c++ 11 - Modo corretto per creare unique_ptr che contiene una matrice allocata




pointer make (5)

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.


Usa la versione dell'array:

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

O con c ++ 14, una forma migliore (VS2013 ce l'ha già):

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]);

Ho usato con successo https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake .

Ho appena seguito le linee guida: ho aggiunto i file alla mia cartella CMAKE_MODULE_PATH

set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
if(CMAKE_COMPILER_IS_GNUCXX)
    include(CodeCoverage)
    setup_target_for_coverage(${PROJECT_NAME}_coverage ${PROJECT_TEST_NAME} coverage)
endif()

nel mio CMakeLists.txt . Ho anche aggiunto manualmente gcov come dipendenza per il mio target:

if(CMAKE_COMPILER_IS_GNUCXX)
    target_link_libraries(${PROJECT_TEST_NAME} gcov)
endif()

Con questo, digito semplicemente

make my_project_coverage

e ottengo il rapporto html nella directory di coverage del mio albero di build.





c++ linux gcc c++11 unique-ptr