我对std::shared_ptr
控制块有2个问题:
(1)关于大小:
如何以编程方式找到std::shared_ptr
的控制块的确切大小?
(2)关于逻辑:
此外,boost::shared_ptr
提到它们相对于控制块的更改是完全无锁的。(从Boost版本1.33.0开始,shared_ptr
在最常见的情况下使用无锁实现平台。)我不认为std::shared_ptr
遵循相同的原则-是否计划在将来的任何C ++版本中使用?这是否还意味着对于多线程情况,boost::shared_ptr
是一个更好的主意?
答案 0 :(得分:5)
控制块未暴露。在实现中,我已经读到,连续存储删除程序(和/或在共享共享的情况下,对象本身)的大小是动态的。
通常,它至少包含3个指针大小的字段-弱,强计数和删除程序调用程序。
至少有一个实施依赖于RTTI;其他人没有。
关于计数的操作在我阅读的实现中使用原子操作;请注意,C ++不需要原子操作都必须是无锁的(我相信没有指针大小的无锁操作的平台可以是兼容的C ++平台)。
它们的状态彼此一致,并且彼此一致,但是没有尝试使它们与对象状态一致。这就是为什么在某些平台上使用原始共享ptr作为写pImpls上的副本可能容易出错的原因。
答案 1 :(得分:5)
(1)关于大小:如何以编程方式找到std :: shared_ptr的控制块的确切大小?
没有办法。无法直接访问。
(2)关于逻辑:另外,boost :: shared_ptr提到它们对于控制块中的更改是完全无锁的。(从Boost版本1.33.0开始,shared_ptr在大多数情况下使用无锁实现。通用平台。)我不认为std :: shared_ptr遵循相同的原则-是否计划在将来的C ++版本中使用?这不是也意味着boost :: shared_ptr对于多线程情况是更好的主意吗?
绝对不是。无锁实现并不总是比使用锁的实现更好。充其量,最多不会使实现变得更糟,但不可能使实现变得更好。
请考虑两个同样胜任的程序员,每个程序员都尽最大努力实现shared_ptr
。一个人必须产生一个无锁的实现。对方完全可以自由运用自己的最佳判断。在所有其他条件相同的情况下,必须产生无锁实现的方法根本不可能产生更好的实现。充其量,无锁的实现是最好的,它们都会产生一个。更糟糕的是,在此平台上,无锁实现具有巨大的缺点,并且一个实现者必须使用一个。好吧。
答案 2 :(得分:1)
(1) 当然最好检查实现,但是您仍然可以从程序中进行一些检查。
控制块是动态分配的,因此要确定其大小,您可以使新运算符重载。
然后,您还可以检查一下std :: make_shared是否为您提供了一些控制块大小的优化。 在适当的实现中,我希望它将进行两个分配(objectA和控制块):
std::shared_ptr<A> i(new A());
但是,这只会进行一次分配(然后将objectA初始化为new):
auto a = std::make_shared<A>();
考虑以下示例:
#include <iostream>
#include <memory>
void * operator new(size_t size)
{
std::cout << "Requested allocation: " << size << std::endl;
void * p = malloc(size);
return p;
}
class A {};
class B
{
int a[8];
};
int main()
{
std::cout << "Sizeof int: " << sizeof(int) << ", A(empty): " << sizeof(A) << ", B(8 ints): " << sizeof(B) << std::endl;
{
std::cout << "Just new:" << std::endl;
std::cout << "- int:" << std::endl;
std::shared_ptr<int> i(new int());
std::cout << "- A(empty):" << std::endl;
std::shared_ptr<A> a(new A());
std::cout << "- B(8 ints):" << std::endl;
std::shared_ptr<B> b(new B());
}
{
std::cout << "Make shared:" << std::endl;
std::cout << "- int:" << std::endl;
auto i = std::make_shared<int>();
std::cout << "- A(empty):" << std::endl;
auto a = std::make_shared<A>();
std::cout << "- B(8 ints):" << std::endl;
auto b = std::make_shared<B>();
}
}
我收到的输出(当然,它是硬件架构和编译器特定的):
Sizeof int: 4, A(empty): 1, B(8 ints): 32
Just new:
- int:
Requested allocation: 4
Requested allocation: 24
int的第一个分配-4个字节,下一个分配给控制块-24个字节。
- A(empty):
Requested allocation: 1
Requested allocation: 24
- B(8 ints):
Requested allocation: 32
Requested allocation: 24
看起来控制块是(最有可能的)24个字节。
这就是为什么要使用make_shared:
Make shared:
- int:
Requested allocation: 24
只有一个分配,即int +控制块= 24字节,比以前少。
- A(empty):
Requested allocation: 24
- B(8 ints):
Requested allocation: 48
这里可以期望56(32 + 24),但看起来实现已优化。如果使用make_shared-控制块中不需要指向实际对象的指针,并且其大小仅为16个字节。
检查控制块大小的其他可能性是:
std::cout<< sizeof(std::enable_shared_from_this<int>);
就我而言:
16
所以我要说的是,控制块的大小为16-24字节,具体取决于创建方式。