web-dev-qa-db-ja.com

割り当てられた配列を保持するunique_ptrを作成する適切な方法

フリーストアに割り当てられた配列を保持するunique_ptrを作成する適切な方法は何ですか? Visual Studio 2013はデフォルトでこれをサポートしていますが、Ubuntuでgccバージョン4.8.1を使用すると、メモリリークと未定義の動作が発生します。

問題は次のコードで再現できます。

#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は次の出力を提供します。

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

T[]専門化の使用:

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

理想的な世界では、newを明示的に使用してunique_ptrをインスタンス化する必要はなく、潜在的な例外安全性の落とし穴を避けることに注意してください。このため、C++ 14はstd::make_unique関数テンプレートを提供します。詳細については this excellent GOTW をご覧ください。構文は次のとおりです。

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

配列バージョンを使用します。

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

または、c ++ 14では、より良い形式です(VS2013には既にあります):

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

最も可能性が高い方法は、std::vector<unsigned char>代わりに

#include <vector>
#include <string>

using namespace std;

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

利点は、これによりエラーが発生しにくくなり、簡単な反復、挿入、容量に達した場合の自動再割り当てなど、あらゆる種類の機能にアクセスできることです。

注意点が1つあります。データを大量に移動する場合は、std::vectorは、データの先頭だけでなくサイズと容量も追跡するため、もう少しコストがかかります。

注:memsetは、カウントがゼロの引数で呼び出すため、何もしません。

14
TemplateRex

間抜けのように思えます、私は私が何を意味するかを説明します

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

投稿の目的は、理解する必要がある隠された小さな微妙なものがあることです。オブジェクトの配列の作成は、unique_ptrのオブジェクト配列と同じです。引数で渡した場合にのみ違いが生じます。 unique_ptrのオブジェクトポインターの配列を作成することも、あまり役に立ちません。したがって、ほとんどのシナリオで使用する必要があるのは2つ以下です。

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