事后看来,给定make_shared
,shared_ptr
是否会有一个构造函数,如果它是用C ++ 11引入的,它会带一个原始指针?
是否存在支持此构造函数的强大论据或用例?
它可以避免使用make_shared
exception-safety和使用shared_ptr
memory allocation/performance advantage的错误记录陷阱。
我认为通过make_shared
进行const
BitsPerByte = 8;
BitsPerInteger = SizeOf(Integer) * BitsPerByte;
type
TBitRange = Byte;
function SetBit(const Value: Integer; const Bit: TBitRange): Integer;
begin
Result := Value or (1 shl (Bit mod BitsPerInteger));
end;
构建的另一个好处是,它可能是引擎盖下的单个指针,降低了它的内存使用并使atomic_compare_exchange之类的东西变得更加简单(可能更有效率)。 (见presentation from C++Now)
修改
我理解一个shared_ptr,它基本上是一个intrusive_ptr(对象和控制块合并)会缺少当前std :: shared_ptr所具有的功能。像:
将对象与控制块分开释放的能力(如果长期存在weak_ptrs,这很好)
与提供原始指针的图书馆的兼容性以及释放它们的责任
使用自定义删除程序保留任意资源的能力(对于非拥有指针,不使用删除程序)
能够在保持父对象存活的同时指向子对象(例如成员)。
我建议的是,这些功能可能不常用(或者在使用它作为raii-wrapper的情况下)可能不是最合适的,以保证额外的费用:
在C ++ 98世界中(引入了shared_ptr),make_shared不太实用且用户友好性较差(缺乏完美的转发需要引用包装器和缺少可变参数模板使得实现笨重)。
答案 0 :(得分:14)
事后看来,给定
make_shared
,shared_ptr
是否会有一个构造函数,如果它是用C ++ 11引入的,它会带一个原始指针?
如果您不控制对象的分配怎么办?如果您需要使用自定义删除器怎么办?如果你需要列表初始化而不是parens怎么办?
这些案件都不是make_shared
处理的。
此外,如果您正在使用weak_ptr
,则shared_ptr
通过make_shared
分配的weak_ptr
不会释放任何内存,直到所有operator new
被销毁为止同样。因此,即使你有一个普通的共享指针,上面没有一个适用,你可能仍然更喜欢原始指针构造函数。
另一种情况是,如果您的类型为operator delete
和make_shared
提供了重载。这些可能使它不适合ulimit -t 1
,因为那些重载不会被调用 - 并且可能它们存在是有原因的。
答案 1 :(得分:13)
您的逻辑问题是相信get
区分托管指针和make_shared
指针的原因是因为make_shared
不可用。因此,如果我们强迫每个人都使用shared_ptr
创建shared_ptr
,我们就不需要这种区别了。
这是不正确的。
您可以实现shared_ptr
基于指针的构造函数,而无需区分。毕竟,在初始创建托管get
时,shared_ptr
指针和托管指针是相同的。如果您希望sizeof(T*)
成为shared_ptr
,则可以让get
从托管块中获取T
指针。这与make_shared
是否嵌入在托管块中无关。
因此,区别确实什么都没有与T
及其将get
嵌入到与托管块相同的内存中的能力。或者更确切地说,缺乏它。
不,托管指针和shared_ptr
指针之间的区别是因为它将功能添加到shared_ptr
而创建的。重要的。你列出了其中一些,但你错过了其他人:
对基类具有shared_ptr<base> p = make_shared<derived>(...);
的能力。那就是:
static_pointer_cast
要做到这一点,必须区分特定实例指向的内容和控制块控制的内容。
dynamic_pointer_cast
和reinterpret_pointer_cast
(以及C ++中的get
17)。这些都依赖于托管指针和enable_shared_from_this
指针之间的区别。
shared_ptr
。一个shared_ptr
,指向一个由get
管理的类型的成员子对象。同样,它要求托管指针与shared_ptr
指针不相同。
您似乎也忽略了管理非您创建的指针的能力。这是关键能力,因为它允许您与其他代码库兼容。在内部,您可以使用shared_ptr
来管理1998年编写的库所做的事情。
按照自己的方式,将代码划分为两个时代:pre-C ++ 11和post-C ++ 11。对于未明确为C ++ 11编写的代码,shared_ptr
将不执行任何操作。
关于将所有这些功能包装成单一类型的事情是这样的:
你不需要另一个。
shared_ptr
,因为它满足了很多需求,几乎可以在任何地方有效地使用。它可能不是绝对最有效的类型,但将几乎在每种情况下完成工作。这样做并不是很慢。
它处理多态的共享所有权。它处理成员对象的共享所有权。它处理您未分配的内存的共享所有权。它处理具有特殊分配/释放需求的内存共享所有权。等等。
如果您需要共享所有权语义,并且需要工作,{{1}}每次都会回来。根据您提出的建议,总会有一些限制,这些都会妨碍您完成工作。
默认情况下,首选应该首选的类型不是。
答案 2 :(得分:5)
std::shared_ptr
不仅仅是在堆上分配对象。
考虑将其用作自动关闭共享文件句柄:
#include <cstdio>
#include <memory>
int main()
{
auto closer = [](FILE* fp) { std::fclose(fp); };
auto fp = std::shared_ptr<FILE>(std::fopen("foo.txt", "r"),
closer);
}