113

What is the proper way to create an unique_ptr that holds an array that is allocated on the free store? Visual studio 2013 supports this by default, but when I use gcc version 4.8.1 on Ubuntu I get memory leaks and undefined behaviour.

The problem can be reproduced with this 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 will give this output:

==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)
1
  • 15
    Do not put diseases in your code. Use std::fill. Commented Jan 27, 2014 at 10:39

6 Answers 6

186

Using the T[] specialisation:

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

Note that, in an ideal world, you would not have to explicitly use new to instantiate a unique_ptr, avoiding a potential exception safety pitfall. To this end, C++14 provides you with the std::make_unique function template. See this excellent GOTW for more details. The syntax is:

auto testData = std::make_unique<unsigned char[]>(16000);
Sign up to request clarification or add additional context in comments.

5 Comments

Just to make sure: Does a unique_ptr of T[], created like shown above, call the correct deleter (i.e. delete []) on the data? I saw a tutorial somewhere where they specified a custom deleter, so I am wondering if that is (still) neccessary or if everything will be properly deleted.
@j00hi Yes, it does. That is the reason for having a specialization.
@juanchopanza std::make_unique<T[]> calls default constructor of T. What happens for basic data-types like for unsigned char above? Does make_unique zero initializes basic data-types? When i print the "testData"(above), it prints nothing.
@kartiktrivikram It value initializes the elements, which for types with a default constructor means they are default constructed, and for built-in types means zero-initialization.
The comment by @maxschlepzig below points out that these two statements are not equivalent because the 'new' version will not initialize the data with zeroes, but the 'std::make_unique' version will. If you don't want to initialize the data because you will overwrite it anyway later, you want C++20's 'std::make_unique_for_overwrite' instead.
51

Use the array version :

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

Or with c++14, a better form ( VS2013 already has it ):

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

2 Comments

The 2 statements are not equivalent. The second one value initializes the array, while the first one creates the array uninitialized. In that sense make_unique isn't always better than constructor plus new. That means for use-cases where value-initialization isn't needed and to be avoided because it is performance critical the constructor version is better. Note that auto testData = unique_ptr<unsigned char[]> { new unsigned char[16000]() ]; is equivalent to the make_unique version.
@maxschlepzig C++20 added std::make_unique_for_overwrite to address this issue.
14

A most likely better way would be to use std::vector<unsigned char> instead

#include <vector>
#include <string>

using namespace std;

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

The advantage is that this is much less error-prone and gives you access to all kinds of features such as easy iteration, insertion, automatic reallocation when capacity has been reached.

There is one caveat: if you are moving your data around a lot, a std::vector costs a little more because it keeps track of the size and capacity as well, rather than only the beginning of the data.

Note: your memset doesn't do anything because you call it with a zero count argument.

8 Comments

std::vector is hands-down much less error-prone than manual memory management. But if the OP knows enough to use a std::unique_ptr, I'd say vector is no longer a clear winner. It probably has a bit over 3x the memory footprint, at minimum. Which can count if you're passing it by value (move) in performance-critical code, for example.
@Angew A more likely scenario is that the OP was used to do raw pointer allocations, has heard about unique_ptr and now wants to "upgrade" to modern C++. Your performance argument is well noted but should not matter for all but the most critical code. unique_ptr plus memset is prone to errors, especially if this would be generalized to non-POD types.
If you're willing to weaken the claim (e.g. "A most likely better way ...") or mention the size/performance difference, you'll get an upvote ;-)
I don't see how moving the data cost more because you simply pass it by reference which is the equivalent of what would happen if you passed around a std::unique_ptr. If you want to copy the data then you may have a slight (tiny) overhead but you also get the option to move the data where appropriate.
@Galik I think the comments by Angew that I incorporate are straightforward: a std::vector is 3 times the size of a std::unique_ptr, but it pays back in terms of convenience and data encapsulation.
|
2

Probably something like the following?

int size = get_size();
int const init_value = 123;

auto p = std::make_unique<int[]>(size);
std::fill_n(p.get(), size, init_value);

Comments

1

Seems like a goofup, i will explain what i mean

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

Objective of the post is that there are hidden small subtle things you need to understand. Creating array of objects is same as object array of unique_ptr. It will make difference only when you pass it in the argument. Creating array of object pointers of unique_ptr is also not very useful. So only below two you need to use in most scenarios.

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

Comments

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

1 Comment

While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.