shared_ptr的原始指针构造函数是一个错误吗?
Was the raw-pointer constructor of shared_ptr a mistake?
事后看来,如果make_shared
,如果引入 C++11,shared_ptr
会有一个接受原始指针的构造函数吗?
是否有支持此构造函数的有力论据或用例?
它将避免有据可查的异常安全和使用make_shared
的内存分配/性能优势。
我相信通过make_shared
要求shared_ptr
构造的另一个优点是它可以是引擎盖下的单个指针,降低其内存使用并使atomic_compare_exchange之类的事情变得更加简单(并且可能更有效)。(见C++现在)的演示文稿)
我知道基本上是intrusive_ptr(对象和控制块合并)的shared_ptr将缺乏当前 std::shared_ptr 具有的功能。喜欢:
-
能够将对象与控制块分开释放(如果您长期存在weak_ptrs,那就太好了)
-
与库的兼容性,为您提供原始指针以及释放它们的责任
-
能够使用自定义删除器(或无删除器,对于非拥有指针)保存任意资源
-
在保持父对象活动状态的同时指向子对象(例如,成员)的能力。
我的建议是,这些功能可能不够普遍使用(或者在将其用作 RAII 包装器的情况下)可能不是最合适的,以保证额外的成本:
- 指向控制块的单独指针
- (可能)更复杂的atomic_compare_exchange逻辑,可能不值得。
在 C++98 世界中(引入shared_ptr),make_shared不太实用且不太用户友好(缺乏完美的转发需要引用包装器,缺少可变参数模板使实现变得笨拙)。
你的逻辑问题在于,你认为shared_ptr
之所以区分托管指针和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
(以及C++17reinterpret_pointer_cast
)。这些都依赖于托管指针和get
指针之间的区别。- 这还包括基类中的
enable_shared_from_this
。
- 这还包括基类中的
-
一个
shared_ptr
,它指向其本身由shared_ptr
管理的类型的成员子对象。同样,它要求托管指针与get
指针不同。
您似乎也轻描淡写地忽略了管理非由您创建的指针的能力。这是一项关键能力,因为它允许您与其他代码库兼容。在内部,您可以使用shared_ptr
来管理由 1998 年编写的库制作的内容。
按照自己的方式,您将代码分为两个时代:C++11 年之前和 C++11 年之后。您的shared_ptr
不会对任何未为 C++11 显式编写的代码执行任何操作。
将所有这些功能打包到一个类型中的事情是这样的:
你不需要另一个。
shared_ptr
,因为它满足了如此多的需求,几乎可以在任何地方有效使用。它可能不是绝对最有效的类型,但几乎可以在每种情况下完成工作。而且这样做并不慢。
它通过多态性处理共享所有权。它处理成员对象的共享所有权。它处理未分配的内存的共享所有权。它处理具有特殊分配/释放需求的内存共享所有权。等等。
如果你需要共享所有权语义,并且你需要它来工作,shared_ptr
每次都会支持你。有了你建议的想法,总会有限制,一些阻碍你完成工作的方式。
默认情况下,应首选有效的类型,而不是无效的类型。
后看来,给定
make_shared
,如果用 C++11 引入,shared_ptr
会有一个接受原始指针的构造函数吗?
如果不控制对象的分配怎么办?如果您需要使用自定义删除器怎么办?如果您需要列表初始化而不是参数怎么办?
这些案件都不是由make_shared
处理的。
此外,如果您使用的是weak_ptr
,则通过make_shared
分配的shared_ptr
不会释放任何内存,直到所有weak_ptr
都被销毁。因此,即使您有一个普通的共享指针,其中上述都不适用,您可能仍然更喜欢原始指针构造函数。
另一种情况是,如果您的类型为operator new
和operator delete
提供重载。这些可能使它不适合make_shared
,因为这些重载不会被调用 - 并且可能它们的存在是有原因的。
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);
}
- 试图修复一个错误,该错误不会让我开始编程其余部分
- MSVC 无法根据模板参数进行数学运算,这是一个错误吗?
- 我正在尝试一个傻瓜 C++ 练习,我遇到了一个错误,说类 'GraduateStudent' 没有任何名为 'advisor' 的字段
- 零四元数和任何向量都不为零的特征积,这是一个错误吗?
- 处理程序的模块列表中有一个错误的模块"WebSocketModule"
- 在 C++ 中使用 "transform" 会给出一个错误,指出这未在作用域中声明
- 在Cython中使用C库时,我遇到了一个错误
- 我需要帮助创建一个评分系统,但它一直给我一个错误,注释掉的整数是给我带来麻烦的部分
- 我试图用c++编写递归fibonacci序列,但当我编译时,我遇到了一个错误
- 从system()调用G++会返回一个错误
- 在Visual Studio中,与std::async一起使用时不调用"thread_local"变量"析构函数,这是一个错误吗?
- 我收到一个错误无效的操作数,类型为 const char [42] 和二进制"运算符+"的双倍数
- 使用声明:GCC 和 Clang 的另一个错误?
- 全球免费给出一个错误.调试器不解释
- 相对于继承的构造函数,gcc 编译器是否还有一个错误?
- 为什么直接传递"this"指针来存档是一个错误,而另一个相同类型的指针是可以的?
- 为什么第三板有一个错误
- 如何编写一个错误结构,该结构可以包含不同的强键枚举作为错误代码
- 使用本地类型声明的G lambda被使用但从未定义 - 确实是一个错误
- 我需要在 Android Studio 中构建 NDK. 但它返回一个错误