Can you allocate an array with something equivalent to make_shared?

22,241

Solution 1

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

Solution 2

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

Solution 3

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);
Share:
22,241
Josh Elias
Author by

Josh Elias

Updated on January 11, 2022

Comments

  • Josh Elias
    Josh Elias over 2 years
    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.

  • Josh Elias
    Josh Elias over 11 years
    I can't specify the size of the array at compile time though?
  • Cubbi
    Cubbi over 11 years
    @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<T> forwards its runtime arguments to the constructor of T. Or just make a shared vector.
  • Josh Elias
    Josh Elias over 11 years
    Yes sorry I did mean runtime. Ah..That's an excellent idea, thank you for educating a noob!
  • Quuxplusone
    Quuxplusone over 9 years
    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<T>(new T) is a red flag because it leaks the new T if shared_ptr's constructor happens to throw a bad_alloc exception.
  • PeteC
    PeteC almost 9 years
    @Quuxplusone there's no leak. The object is deleted if an exception (e.g. bad_alloc) is thrown.
  • Quuxplusone
    Quuxplusone almost 9 years
    @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(<ptr to T>), sh_ptr(<ptr to U>). If either of the middle two calls throw, then new U is leaked. herbsutter.com/gotw/_102
  • Dongwei Wang
    Dongwei Wang over 6 years
    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<char, 64> of just the first element in std::array<char, 64>.
  • SagiLow
    SagiLow over 6 years
    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.
  • seattlecpp
    seattlecpp over 4 years
    This method also doesn't allocate the reference count with the array, so it costs two calls into the memory manager.
  • Tony Wall
    Tony Wall about 4 years
    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<T[]>(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.
  • Thomas
    Thomas over 3 years
    even if you do want it shared, make_unique will work std::shared_ptr<char[]> b; b = std::make_unique<char[]>(25);
  • Paul Groke
    Paul Groke over 3 years
    @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.
  • Thomas
    Thomas over 3 years
    @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
  • yoni
    yoni over 3 years
    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*
  • Cubbi
    Cubbi over 3 years
    @yoni no, a dynamic array of float is called vector<float>. Shared pointers aren't related or relevant.