关于shared_ptr引用计数块



我有两个关于std::shared_ptr控制块的问题:

(1(关于大小:如何用程序找到std::shared_ptr控制块的确切大小?

(2(关于逻辑:此外,boost::shared_ptr提到它们相对于控制块中的改变是完全无锁定的。(从Boost版本1.33.0开始,shared_ptr在大多数常见平台上使用了无锁实现。(我不认为std::shared_ptr也遵循同样的做法-这是否适用于未来的C++版本?这难道不意味着boost::shared_ptr对于多线程情况来说是一个更好的主意吗?

(1(关于大小:如何通过程序找到std::shared_ptr的控制块的确切大小?

没有办法。它无法直接访问。

(2(关于逻辑:此外,boost::shared_ptr提到,对于控制块中的更改,它们是完全无锁定的。(从Boost版本1.33.0开始,shared_ptr在大多数常见平台上都使用了无锁实现?这难道不意味着boost::shared_ptr对于多线程情况来说是一个更好的主意吗?

绝对不是。无锁实现并不总是比使用锁的实现更好。有一个额外的约束充其量不会使实现变得更糟,但也不可能使实现变得更好。

考虑两个同样有能力的程序员,每个人都在尽最大努力实现shared_ptr。必须生成一个无锁实现。另一方可以完全自由地使用他们的最佳判断。在其他条件相同的情况下,必须产生无锁实现的实现不可能产生更好的实现。充其量,一个无锁的实现是最好的,它们都会产生一个。更糟糕的是,在这个平台上,无锁实现有巨大的缺点,并且一个实现者必须使用一个。恶心。

控制块未公开。在我读过的实现中,连续存储deleter(和/或,在使共享的情况下,存储对象本身(在大小上是动态的。

通常,它至少包含3个指针大小的字段——弱、强计数和deleter调用程序。

至少有一个实现依赖于RTTI;其他人则不然。

计数操作在我读过的实现中使用原子操作;注意,C++并不要求原子操作都是无锁的(我相信一个没有指针大小无锁操作的平台可以是一个兼容的C++平台(。

它们的状态是相互一致的,也与它们自身一致,但没有试图使它们与对象状态一致。这就是为什么在某些平台上使用原始共享ptr作为写时复制pImpls可能容易出错的原因。

(1(当然,最好检查实现,但是您仍然可以从程序中进行一些检查。

控制块是动态分配的,所以为了确定它的大小,您可能会重载新的运算符。

然后,您还可以检查std::make_shared是否为您提供了一些控制块大小的优化。在正确的实现中,我希望这将进行两个分配(对象A和控制块(:

std::shared_ptr<A> i(new A());

然而,这将只进行一次分配(然后用新的放置初始化对象A(:

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字节,这取决于它是如何创建的。

最新更新