102

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)
lauw
  • 1,301
  • 2
  • 10
  • 10

6 Answers6

178

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);
einpoklum
  • 118,144
  • 57
  • 340
  • 684
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • 3
    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 Dec 29 '18 at 17:32
  • 2
    @j00hi Yes, it does. That is the reason for having a specialization. – juanchopanza Dec 29 '18 at 21:11
  • 2
    @juanchopanza std::make_unique 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. – karthiksatyanarayana May 14 '19 at 07:07
  • 4
    @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. – juanchopanza May 14 '19 at 20:52
49

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 );
Gaspa79
  • 5,488
  • 4
  • 40
  • 63
galop1n
  • 8,573
  • 22
  • 36
  • 28
    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 { new unsigned char[16000]() ];` is equivalent to the `make_unique` version. – maxschlepzig Nov 28 '16 at 07:05
  • 2
    @maxschlepzig C++20 added [`std::make_unique_for_overwrite`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique) to address this issue. – Max Truxa Jan 12 '23 at 10:47
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.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 2
    `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 is no longer proud of SO Jan 27 '14 at 10:13
  • 3
    @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. – TemplateRex Jan 27 '14 at 10:30
  • 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 ;-) – Angew is no longer proud of SO Jan 27 '14 at 10:36
  • 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 Jun 28 '16 at 11:13
  • @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. – TemplateRex Jun 28 '16 at 12:38
  • I have a use case which std::vector won't satisfy. I want to create an array which acts as a memory buffer. I give meaning to the buffer by casting and then holding references to its internals. The size is dynamic, but does not change after creation. If I use std::vector I am at risk of having the vector resize and invalidate my references. I'm also unable to return the buffer from a function because std::move does not guarantee that the references to vector internals will remain valid (although it will in most implementations). – user2445507 Jun 11 '19 at 16:30
  • For example, I would return something like this from my function: struct MemoryBuffer { std::unique_ptr buffer; Interface& refToInternals; }; – user2445507 Jun 11 '19 at 16:33
  • @TemplateRex Just came across this while wanting a very large array of heap allocated `std::atomic` . `std::vector` refuses to accept atomics (without bending over backwards with a wrapper). So there remain good use cases for large `array`? – Oliver Schönrock Jan 13 '20 at 22:48
2
unsigned int size=16000;
std::unique_ptr<unsigned char[], std::default_delete<unsigned char[]>> pData(new unsigned char[size]);
  • 4
    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. – adiga Nov 23 '17 at 07:15
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);
1

Probably something like the following?

using namespace std;

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

unique_ptr<int[]> p = make_unique<int[]>(size)
fill(p.get(), p.get() + size, init_value);
bruin
  • 979
  • 1
  • 10
  • 30