フリーストアに割り当てられた配列を保持する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)
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);
配列バージョンを使用します。
auto testData = std::unique_ptr<unsigned char[]>{ new unsigned char[16000] };
または、c ++ 14では、より良い形式です(VS2013には既にあります):
auto testData = std::make_unique<unsigned char[]>( 16000 );
最も可能性が高い方法は、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
は、カウントがゼロの引数で呼び出すため、何もしません。
間抜けのように思えます、私は私が何を意味するかを説明します
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);
unsigned int size=16000;
std::unique_ptr<unsigned char[], std::default_delete<unsigned char[]>> pData(new unsigned char[size]);