struct A;
std::shared_ptr<A> p1 = std::make_shared<A>();
std::shared_ptr<A> p2(new A);
上面两者有什么区别呢? 区别是:std::shared_ptr构造函数会执行两次内存申请,而std::make_shared则执行一次。
std::shared_ptr在实现的时候使用的refcount技术,因此内部会有一个计数器(控制块,用来管理数据)和一个指针,指向数据。因此在执行std::shared_ptr<A> p2(new A)
的时候,首先会申请数据的内存,然后申请内控制块,因此是两次内存申请,而std::make_shared<A>()
则是只执行一次内存申请,将数据和控制块的申请放到一起。那这一次和两次的区别会带来什么不同的效果呢?
异常安全
考虑下面一段代码:
void f(std::shared_ptr<Lhs> &lhs, std::shared_ptr<Rhs> &rhs){...}
f(std::shared_ptr<Lhs>(new Lhs()),
std::shared_ptr<Rhs>(new Rhs())
);
因为C++允许参数在计算的时候打乱顺序,因此一个可能的顺序如下:
- new Lhs()
- new Rhs()
- std::shared_ptr
- std::shared_ptr
此时假设第2步出现异常,则在第一步申请的内存将没处释放了,上面产生内存泄露的本质是当申请数据指针后,没有马上传给std::shared_ptr,因此一个可能的解决办法
auto lhs = std::shared_ptr<Lhs>(new Lhs());
auto rhs = std::shared_ptr<Rhs>(new Rhs());
f(lhs, rhs);
而一个比较好的方法是使用std::make_shared
。
f(std::make_shared<Lhs>(),
std::make_shared<Rhs>()
);
make_shared的缺点
因为make_shared只申请一次内存,因此控制块和数据块在一起,只有当控制块中不再使用时,内存才会释放,但是weak_ptr却使得控制块一直在使用。