是make_shared真的比新的更有效吗? [英] Is make_shared really more efficient than new?

查看:1160
本文介绍了是make_shared真的比新的更有效吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C ++ 11中的 shared_ptr make_shared ,并编写了一个玩具示例,实际上是在调用 make_shared 时发生的。作为基础设施,我使用llvm / clang 3.0以及XCode4中的llvm std c ++库。

  class Object 
{
public:
Object(const string& str)
{
cout< 构造器<< str<< endl;
}

Object()
{
cout< Default constructor<< endl;

}

〜Object()
{
cout< Destructor< endl;
}

Object(const Object& rhs)
{
cout< 复制构造函数...< endl;
}
};

void make_shared_example()
{
cout< 使用make_shared创建smart_ptr ...< endl;
auto ptr_res1 = make_shared< Object>(make_shared);
cout<< 使用make_shared:done创建smart_ptr。 << endl;

cout<< 创建smart_ptr使用新...< endl;
auto ptr_res2(new Object(new));
cout<< 使用new:done创建smart_ptr。 << endl;
}

现在看看输出,请:


使用make_shared ...创建smart_ptr



构造函数make_shared



复制构造函数...



复制构造函数...



破坏者



使用make_shared:done创建smart_ptr。



smart_ptr using new ...



构造函数new



使用new:done创建smart_ptr。 b
$ b

解构函数



析构函数


看来 make_shared 正在调用复制构造函数两次。如果我为一个 Object 使用常规分配内存,这不会发生,只有一个对象构建。



我想知道的是以下内容。我听说 make_shared 应该比使用 new [1],[2]更有效。一个原因是因为 make_shared 将引用计数与要在同一内存块中管理的对象一起分配。好的,我得到了点。这当然比两个单独的分配操作更有效。



相反,我不明白为什么这必须伴随着对复制构造函数的两次调用的成本 Object 。因为这个,我不相信 make_shared 比在 中使用 new > case。我错了吗?好的,可以实现 Object 的移动构造函数,但仍然不确定这是否比分配对象 new 。至少不是在每一种情况。如果复制 Object 比为参考计数器分配内存更便宜,这是真的。但是 shared_ptr - 内部引用计数器可以使用几个原始数据类型实现,<?p>

帮助和解释为什么 make_shared 是效率的方式去,尽管概述了副本开销?



-



[1] http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared



[ 2] http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/ STL11-Magic-Secrets (约分钟10)



EDIT



你是对的,输出不匹配发布的代码。代码应该是

  void make_shared_example()
{
cout< 使用make_shared创建smart_ptr ...< endl;
auto ptr_res1 = make_shared< Object>(make_shared);
cout<< 使用make_shared:done创建smart_ptr。 << endl;

cout<< 创建smart_ptr使用新...< endl;
shared_ptr< Object> ptr_res2(new Object(new));
cout<< 使用new:done创建smart_ptr。 << endl;
}

第二个 auto 是一个错误。抱歉,

解决方案


作为基础设施,我使用llvm / clang 3.0以及llvm std XCode4中的c ++库。


这似乎是你的问题。 C ++ 11标准规定 make_shared< T> (和 allocate_shared )的以下要求第20.7.2.2.6节:


需要:表达式:: new(pv)T(std :: forward(args) 。),其中pv的类型为void *,并指向适合保存类型为T的对象的存储,应该很好地形成。 A应是分配器(17.6.3.5)。 A的复制构造函数和析构函数不会抛出异常。


不是需要是可复制构造的。事实上, T 甚至不需要为非展示位置新的可构造。它只需要在现场可构造。这意味着 make_shared< T> 可以使用 T 的只有 / code>它就位。



所以你得到的结果与标准不一致。 LLVM的libc ++在这方面被打破了。提交错误报告。



以下是将代码加入VC2010时发生的情况:

 使用make_shared创建smart_ptr ... 
构造函数make_shared
使用make_shared:done创建smart_ptr。
使用new ...创建smart_ptr
构造函数new
使用new:done创建smart_ptr。
析构函数
析构函数



我也将它移植到Boost的原始 shared_ptr make_shared ,我得到了与VC2010相同的东西。



我建议提交一个错误报告,因为libc ++的行为坏了。


I was experimenting with shared_ptr and make_shared from C++11 and programmed a little toy example to see what is actually happening when calling make_shared. As infrastructure I was using llvm/clang 3.0 along with the llvm std c++ library within XCode4.

class Object
{
public:
    Object(const string& str)
    {
        cout << "Constructor " << str << endl;
    }

    Object()
    {
        cout << "Default constructor" << endl;

    }

    ~Object()
    {
        cout << "Destructor" << endl;
    }

    Object(const Object& rhs)
    {
        cout << "Copy constructor..." << endl;
    }
};

void make_shared_example()
{
    cout << "Create smart_ptr using make_shared..." << endl;
    auto ptr_res1 = make_shared<Object>("make_shared");
    cout << "Create smart_ptr using make_shared: done." << endl;

    cout << "Create smart_ptr using new..." << endl;
    auto ptr_res2(new Object("new"));
    cout << "Create smart_ptr using new: done." << endl;
}

Now have a look at the output, please:

Create smart_ptr using make_shared...

Constructor make_shared

Copy constructor...

Copy constructor...

Destructor

Destructor

Create smart_ptr using make_shared: done.

Create smart_ptr using new...

Constructor new

Create smart_ptr using new: done.

Destructor

Destructor

It appears that make_shared is calling the copy constructor two times. If I allocate memory for an Object using a regular new this does not happen, only one Object is constructed.

What I am wondering about is the following. I heard that make_shared is supposed to be more efficient than using new[1], [2]. One reason is because make_shared allocates the reference count together with the object to be managed in the same block of memory. OK, I got the point. This is of course more efficient than two separate allocation operations.

On the contrary I don't understand why this has to come with the cost of two calls to the copy constructor of Object. Because of this I am not convinced that make_shared is more efficient than allocation using new in every case. Am I wrong here? Well OK, One could implement a move constructor for Object but still I am not sure whether this this is more efficient than just allocating Object through new. At least not in every case. It would be true if copying Object is less expensive than allocating memory for a reference counter. But the shared_ptr-internal reference counter could be implemented using a couple of primitive data types, right?

Can you help and explain why make_shared is the way to go in terms of efficiency, despite the outlined copy overhead?

--

[1] http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared

[2] http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/STL11-Magic-Secrets (around minute 10)

EDIT

You are right, the output does not match the code posted. the code should rather be

void make_shared_example()
{
    cout << "Create smart_ptr using make_shared..." << endl;
    auto ptr_res1 = make_shared<Object>("make_shared");
    cout << "Create smart_ptr using make_shared: done." << endl;

    cout << "Create smart_ptr using new..." << endl;
    shared_ptr<Object> ptr_res2(new Object("new"));
    cout << "Create smart_ptr using new: done." << endl;
}

The second auto was a mistake. Sorry about that.

解决方案

As infrastructure I was using llvm/clang 3.0 along with the llvm std c++ library within XCode4.

Well that appears to be your problem. The C++11 standard states the following requirements for make_shared<T> (and allocate_shared<T>), in section 20.7.2.2.6:

Requires: The expression ::new (pv) T(std::forward(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.

T is not required to be copy-constructable. Indeed, T isn't even required to be non-placement-new constructable. It is only required to be constructable in-place. This means that the only thing that make_shared<T> can do with T is new it in-place.

So the results you get are not consistent with the standard. LLVM's libc++ is broken in this regard. File a bug report.

For reference, here's what happened when I took your code into VC2010:

Create smart_ptr using make_shared...
Constructor make_shared
Create smart_ptr using make_shared: done.
Create smart_ptr using new...
Constructor new
Create smart_ptr using new: done.
Destructor
Destructor

I also ported it to Boost's original shared_ptr and make_shared, and I got the same thing as VC2010.

I'd suggest filing a bug report, as libc++'s behavior is broken.

这篇关于是make_shared真的比新的更有效吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆