use*_*354 50 c++ clang shared-ptr make-shared libc++
我与尝试shared_ptr,并make_shared从C++ 11和编程的小玩具的例子来看看调用时什么是实际发生的事情make_shared.作为基础设施,我使用llvm/clang 3.0以及XCode4中的llvm std c ++库.
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;
shared_ptr<Object> ptr_res2(new Object("new"));
cout << "Create smart_ptr using new: done." << endl;
}
Run Code Online (Sandbox Code Playgroud)
现在看一下输出,请:
使用make_shared创建smart_ptr ...
构造函数make_shared
复制构造函数...
复制构造函数...
析构函数
析构函数
使用make_shared:done创建smart_ptr.
使用new创建smart_ptr ...
构造函数新
使用new:done创建smart_ptr.
析构函数
析构函数
似乎make_shared是两次调用复制构造函数.如果我为Object使用常规分配内存,则new不会发生,只Object构造一个.
我想知道的是以下内容.听说make_shared应该是比使用更有效的new(1,2).一个原因是因为make_shared在同一内存块中将引用计数与要管理的对象一起分配.好的,我明白了.这当然比两个单独的分配操作更有效.
相反,我不明白为什么这需要两次调用复制构造函数的成本Object.因此,我不相信在每种情况下make_shared都比使用分配更有效.我错了吗?好的,一个人可以实现一个移动构造函数,但我仍然不确定这是否比分配通过更有效.至少在每种情况下都不是.如果复制比为参考计数器分配内存便宜,那将是真的.但是- 内部参考计数器可以使用几种原始数据类型来实现,对吧?newObjectObjectnewObjectshared_ptr
make_shared尽管概述了复制开销,你能帮助并解释为什么在效率方面走的路?
Nic*_*las 37
作为基础设施,我使用llvm/clang 3.0以及XCode4中的llvm std c ++库.
那似乎是你的问题.C++ 11标准在第20.7.2.2.6节中规定了make_shared<T>(和allocate_shared<T>)的以下要求:
要求:表达式:: new(pv)T(std :: forward(args)...),其中pv具有类型void*并且指向适合于保存类型T的对象的存储,应该很好地形成.A应为分配器(17.6.3.5).A的拷贝构造函数和析构函数不应抛出异常.
T是不是需要被复制施工的.实际上,T甚至不需要是非安置新的可构造的.它只需要就地构建.这意味着唯一make_shared<T>可以做到的T就是new就地.
所以你得到的结果与标准不一致.在这方面,LLVM的libc ++被打破了.提交错误报告.
作为参考,这是我将代码带入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
Run Code Online (Sandbox Code Playgroud)
我也把它移植到升压转换器的原始shared_ptr和make_shared,和我同样的事情,VC2010.
我建议提交一份错误报告,因为libc ++的行为已被破坏.
Ker*_* SB 32
你必须比较这两个版本:
std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));
Run Code Online (Sandbox Code Playgroud)
在你的代码中,第二个变量只是一个裸指针,而不是一个共享指针.
现在就肉了.make_shared 是(在实践中)更有效,因为它具有在一个单一的动态分配的实际对象一起分配参照控制块.相比之下,构造函数shared_ptr采用裸对象指针必须为引用计数分配另一个动态变量.权衡是make_shared(或其表兄allocate_shared)不允许您指定自定义删除器,因为分配是由分配器执行的.
(这不会影响对象本身的构造.从Object两个版本来看,两个版本之间没有区别.更有效的是共享指针本身,而不是托管对象.)
因此,要记住的一件事是优化设置.在没有优化的情况下,测量性能,特别是关于c ++的性能是没有意义的.我不知道你是否真的用优化编译,所以我认为值得一提.
这就是说,你用这个测试测量是不是一种方式,make_shared是更有效的.简单地说,你正在测量错误的东西:-P.
这是交易.通常,当您创建共享指针时,它至少有2个数据成员(可能更多).一个用于指针,一个用于引用计数.这个引用计数在堆上分配(这样它就可以在shared_ptr不同的生命周期之间共享......毕竟这就是重点!)
因此,如果您正在创建一个类似于std::shared_ptr<Object> p2(new Object("foo"));至少有2个调用的对象new.一个用于Object和一个用于引用计数对象.
make_shared有一个选项(我不确定它必须),做一个new足够大的单一来保持指向的对象和引用计数在同一个连续的块中.有效地分配一个看起来像这样的对象(说明性的,而不是字面意思).
struct T {
int reference_count;
Object object;
};
Run Code Online (Sandbox Code Playgroud)
由于引用计数和对象的生命周期是捆绑在一起的(一个人比另一个人活得更久没有意义).整个块也可以delete同时进行.
所以效率是分配,而不是复制(我怀疑与优化相比,其他任何事情都是如此).
要明确的是,这就是提升所要说的 make_shared
http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/make_shared.html
除了方便和风格之外,这样的函数也是异常安全且相当快的,因为它可以对对象及其相应的控制块使用单个分配,从而消除了shared_ptr的构造开销的很大一部分.这消除了关于shared_ptr的主要效率投诉之一.
| 归档时间: |
|
| 查看次数: |
37556 次 |
| 最近记录: |