43
buffer = new char[64];
buffer = std::make_shared<char>(char[64]); ???

Can you allocate memory to an array using make_shared<>()?

I could do: buffer = std::make_shared<char>( new char[64] );

But that still involves calling new, it's to my understanding make_shared is safer and more efficient.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
Josh Elias
  • 3,250
  • 7
  • 42
  • 73

4 Answers4

35

Do you need the allocated memory to be shared? You can use a std::unique_ptr instead and the std::make_unique available on C++14:

auto buffer = std::make_unique<char[]>(64);

There will be a std::make_shared version available in C++20:

auto buffer = std::make_shared<char[]>(64);
Myon
  • 937
  • 13
  • 23
  • 2
    even if you do want it shared, `make_unique` will work `std::shared_ptr b; b = std::make_unique(25);` – Thomas Oct 06 '20 at 21:36
  • 1
    @Thomas Yes, `make_unique` will work just fine. But initializing a `shared_ptr` that way will cost you an extra memory allocation. Which can be avoided when using `make_shared`. – Paul Groke Nov 18 '20 at 18:30
  • @PaulGroke True, but for completeness sake I'll add that that saved allocation comes with a slight risk [weak_ptr, make_shared and memory deallocation](https://stackoverflow.com/questions/32113594/weak-ptr-make-shared-and-memory-deallocation) – Thomas Nov 18 '20 at 18:43
  • @Thomas Is it fine to remove the semicolon and have it all in one statement? The [comment here](https://stackoverflow.com/questions/32113594/weak-ptr-make-shared-and-memory-deallocation#comment52122540_32113594) seems to indicate that it is fine, but wanted to confirm. – Venryx Jul 28 '23 at 12:42
  • 1
    yes, it can be written as `std::shared_ptr b = std::make_unique(25);` – Thomas Jul 29 '23 at 15:02
30

The point of make_shared is to incorporate the managed object into the control block of the shared pointer,

Since you're dealing with C++11, perhaps using a C++11 array would satisfy your goals?

#include <memory>
#include <array>
int main()
{
    auto buffer = std::make_shared<std::array<char, 64>>();
}

Note that you can't use a shared pointer the same way as a pointer you'd get from new[], because std::shared_ptr (unlike std::unique_ptr, for example) does not provide operator[]. You'd have to dereference it: (*buffer)[n] = 'a';

Cubbi
  • 46,567
  • 13
  • 103
  • 169
  • 1
    I can't specify the size of the array at compile time though? – Josh Elias Dec 10 '12 at 03:33
  • 3
    @JoshElias You sure can at compile time. Do you mean runtime? That would require your own class that takes array size as a constructor argument, since `make_shared` forwards its runtime arguments to the constructor of `T`. Or just make a shared vector. – Cubbi Dec 10 '12 at 14:18
  • 1
    Yes sorry I did mean runtime. Ah..That's an excellent idea, thank you for educating a noob! – Josh Elias Dec 10 '12 at 16:07
  • 3
    Upvoted for insight, but you should know that `make_shared` isn't *just* for premature optimization; it's also for exception-safety. `std::shared_ptr(new T)` is a red flag because it leaks the `new T` if `shared_ptr`'s constructor happens to throw a `bad_alloc` exception. – Quuxplusone Nov 05 '14 at 00:21
  • @Quuxplusone there's no leak. The object is deleted if an exception (e.g. bad_alloc) is thrown. – PeteC Jun 29 '15 at 10:41
  • 1
    @PeteC we're both right, in that the problem case is subtler than my terse comment had implied. The problem is with an expression such as `foo(sh_ptr(new T), sh_ptr(new U));` — where there's no sequence point between the calls to `sh_ptr`'s constructor, so a valid order of construction is `new U`, `new T`, `sh_ptr()`, `sh_ptr()`. If either of the middle two calls throw, then `new U` is leaked. http://herbsutter.com/gotw/_102/ – Quuxplusone Jun 29 '15 at 17:21
  • I have a question about the memory release for useing a shared pointer points to a std::array. Shared pointer is not good to delete arrays. we have to define the deleter to make it works correctly. For the code in your reply, if the shared pointer is out of scope, it will automatically delete all memory of std::array of just the first element in std::array. – Dongwei Wang Oct 08 '17 at 22:49
  • So you are telling me that if I want a dynamic array of float indeed to: Create my class that inherits from std::array and put that as a parameter for the shred ptr? I'd rather take my chances with float* – yoni Nov 25 '20 at 16:23
  • @yoni no, a dynamic array of float is called vector. Shared pointers aren't related or relevant. – Cubbi Nov 25 '20 at 16:58
7

How about this?

template<typename T>
inline std::shared_ptr<T> MakeArray(int size)
{
    return std::shared_ptr<T>( new T[size], []( T *p ){ delete [] p; } );
}

auto  buffer = new char[64];
auto  buffer = MakeArray<char>(64);
ToyAuthor X
  • 315
  • 4
  • 5
  • 3
    There is a difference between creating new `shared_ptr` and using `make_shared`, the later has 1 less atomic operation and it's preferred whenever possible. – SagiLow Oct 23 '17 at 09:15
  • 1
    This method also doesn't allocate the reference count with the array, so it costs two calls into the memory manager. – seattlecpp Dec 06 '19 at 00:29
  • 1
    Considering C++20 is around the corner and this "polyfill" allows us to write code very close to what it will look like once your compiler of choice supports C++20's make_shared(size_t), the overhead will be greatly outweighed by more clean robust future-proof code. If you rename the template to something like "make_shared_array" it's obvious what the intent was when you come back to your code next year to upgrade it ;-) This helps greatly working with variable structure buffers, as many system calls require. The other variable array methods generate blocking cast warnings=errors. – Tony Wall Apr 17 '20 at 13:35
0

The most efficient way is to use the make_shared() overload below, which is available in Boost and C++20.

template< class T >
shared_ptr<T> make_shared( std::size_t N );
cigien
  • 57,834
  • 11
  • 73
  • 112
dshvets1
  • 119
  • 1
  • 3