std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));

许多谷歌和stackoverflow的帖子都在这里,但我不明白为什么make_shared比直接使用shared_ptr更有效。

有人能一步一步地向我解释创建的对象序列和两者所做的操作,这样我就能理解make_shared是如何高效的。我在上面举了一个例子供大家参考。


当前回答

共享指针既管理对象本身,也管理包含引用计数和其他管理数据的小对象。Make_shared可以分配一个内存块来存储这两个对象;从指向已分配对象的指针构造共享指针需要分配第二个块来存储引用计数。

除了这种效率之外,使用make_shared意味着你根本不需要处理新的和原始的指针,从而提供了更好的异常安全性——不可能在分配对象之后,但在将其分配给智能指针之前抛出异常。

其他回答

在前面提到的情况之上,还有另一种情况是两种可能性不同的:如果需要调用非公共构造函数(受保护的或私有的),make_shared可能无法访问它,而带有新变体的可以正常工作。

class A
{
public:

    A(): val(0){}

    std::shared_ptr<A> createNext(){ return std::make_shared<A>(val+1); }
    // Invalid because make_shared needs to call A(int) **internally**

    std::shared_ptr<A> createNext(){ return std::shared_ptr<A>(new A(val+1)); }
    // Works fine because A(int) is called explicitly

private:

    int val;

    A(int v): val(v){}
};

如果你需要shared_ptr控制的对象上的特殊内存对齐,你不能依赖make_shared,但我认为这是不使用它的唯一一个好理由。

Shared_ptr:执行两个堆分配

控制块(引用计数) 正在管理的对象

Make_shared:只执行一次堆分配

控制块和对象数据。

共享指针既管理对象本身,也管理包含引用计数和其他管理数据的小对象。Make_shared可以分配一个内存块来存储这两个对象;从指向已分配对象的指针构造共享指针需要分配第二个块来存储引用计数。

除了这种效率之外,使用make_shared意味着你根本不需要处理新的和原始的指针,从而提供了更好的异常安全性——不可能在分配对象之后,但在将其分配给智能指针之前抛出异常。

关于效率和花费在分配上的时间,我做了下面这个简单的测试,我通过这两种方法创建了许多实例(一次一个):

for (int k = 0 ; k < 30000000; ++k)
{
    // took more time than using new
    std::shared_ptr<int> foo = std::make_shared<int> (10);

    // was faster than using make_shared
    std::shared_ptr<int> foo2 = std::shared_ptr<int>(new int(10));
}

问题是,使用make_shared比使用new花费了两倍的时间。因此,使用new会有两个堆分配,而不是使用make_shared分配一个。也许这是一个愚蠢的测试,但它不是表明使用make_shared比使用new花费更多的时间吗?当然,我说的是只用的时间。