你能否使用类似于make_shared的方式分配一个数组?

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

你能否使用make_shared<>()为数组分配内存?

我可以这样做:buffer = std::make_shared<char[]>( new char[64] );

但这仍然涉及调用new,据我所知,make_shared更安全、更高效。


3
std::vector<char> buffer(64); - Jonathan Wakely
不,你不能执行“std::make_shared<char>(new char[64])”。 - ABCD
4个回答

35

你需要共享分配的内存吗?你可以使用 std::unique_ptr 来代替,并且在 C++14 上提供了 std::make_unique :

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

在C++20中将提供一个std::make_shared版本:

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

2
即使您想共享它,make_unique 也可以工作。std::shared_ptr<char[]> b; b = std::make_unique<char[]>(25); - Thomas
1
@Thomas 是的,make_unique 可以正常工作。但是用那种方式初始化 shared_ptr 会多消耗一次内存分配。使用 make_shared 可以避免这种情况。 - Paul Groke
@PaulGroke 没错,但为了完整起见,我会补充一下,这种节省的分配带有轻微的风险weak_ptr、make_shared和内存释放 - Thomas
@Thomas 去掉分号,将其合并为一个语句是否可以?这里的评论似乎表明可以,但想确认一下。 - Venryx
1
是的,它可以写成std::shared_ptr<char[]> b = std::make_unique<char[]>(25); - Thomas

30

make_shared的目的是将被管理的对象整合到shared pointer的控制块中,

如果您正在处理C++11,则使用C++11数组可能会满足您的目标。

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

请注意,您不能像使用从new[]获得的指针那样使用共享指针,因为std::shared_ptr(与例如std::unique_ptr不同)不提供operator[]。您必须对其进行解引用:(*buffer)[n] = 'a';


1
我无法在编译时指定数组的大小吗? - Josh Elias
3
你当然可以在编译时进行操作。你是指运行时吗?那将需要你自己编写一个类,将数组大小作为构造函数参数,因为make_shared<T>会将其运行时参数转发到T的构造函数中。或者只需创建一个共享的向量即可。 - Cubbi
1
是的,抱歉我是指运行时。啊...这是一个绝妙的想法,谢谢你教育一个新手! - Josh Elias
3
感谢您的深入见解,但您应该了解 make_shared 不仅仅是为了过早优化;它还可以用于异常安全。使用 std::shared_ptr<T>(new T) 是一个警告信号,因为如果 shared_ptr 的构造函数抛出 bad_alloc 异常,它会泄漏 new T - Quuxplusone
1
@PeteC我们两个都是对的,问题案例比我简短的评论所暗示的要微妙得多。问题出在这样一个表达式上:foo(sh_ptr(new T), sh_ptr(new U)); —— 在调用sh_ptr的构造函数之间没有序列点,因此构造的有效顺序是new U,new T,sh_ptr(<ptr to T>),sh_ptr(<ptr to U>)。如果中间两个调用中有任何一个抛出异常,则会泄漏new U。http://herbsutter.com/gotw/_102/ - Quuxplusone
显示剩余4条评论

7

这个怎么样?

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

3
创建新的shared_ptr和使用make_shared之间存在差异,后者少了1次原子操作,尽可能使用后者。 - SagiLow
1
该方法也不会为数组分配引用计数,因此它会导致两次内存管理器的调用成本。 - seattlecpp
1
考虑到 C++20 已经在拐角处,这个“polyfill”的作用就是允许我们编写非常接近于您所选择的编译器支持 C++20 的 make_shared<T[]>(size_t) 的代码,这样做的开销将大大被更干净、更健壮、具有未来性的代码所抵消。如果你将模板命名为像 “make_shared_array” 这样的东西,当你明年回来升级代码时,它的意图就显而易见了;-) 这对于使用可变结构缓冲区非常有帮助,因为许多系统调用都需要这样做。其他可变数组方法会生成阻止转换警告=错误。 - Tony Wall

1
最有效的方法是使用下面的make_shared()重载,它在Boost和C++20中都可用。
template< class T >
shared_ptr<T> make_shared( std::size_t N );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接