为什么在所有情况下都允许指向 shared_ptr 构造的原始指针?
Why is raw pointer to shared_ptr construction allowed in all cases?
我正在阅读 Top 10 dumb mistakes to avoid with C++11 smart pointer。
第 5 号显示:
Mistake # 5 : Not assigning an object(raw pointer) to a shared_ptr as
soon as it is created !
int main()
{
Aircraft* myAircraft = new Aircraft("F-16");
shared_ptr<aircraft> pAircraft(myAircraft);
...
shared_ptr<aircraft> p2(myAircraft);
// will do a double delete and possibly crash
}
推荐如下:
Use make_shared
or new
and immediately construct the pointer with
it.
好的,毫无疑问,问题和建议。
但是我对 shared_ptr
的 design 有疑问。
这是一个很容易犯的错误,shared_ptr
的整个“安全”设计可能会被非常容易检测到的误用所抛弃。
现在的问题是,是否可以通过 shared_ptr
的替代设计轻松解决此问题,其中来自原始指针的唯一构造函数是来自右值引用的构造函数?
template<class T>
struct shared_ptr{
shared_ptr(T*&& t){...basically current implementation...}
shared_ptr(T* t) = delete; // this is to...
shared_ptr(T* const& t) = delete; // ... illustrate the point.
shared_ptr(T*& t) = delete;
...
};
这样 shared_ptr
只能从 new
或一些工厂函数的结果中初始化。
这是库中 C++ 语言的未充分利用吗?
或 如果这很可能是误用,那么从原始指针(左值)引用获取构造函数有什么意义?
这是历史事故吗? (例如 shared_ptr 是在引入 r 值引用之前提出的,等等)向后兼容?
(当然有人会说 std::shared_ptr<type>(std::move(ptr));
这更容易捕捉,并且如果确实有必要的话也是一种解决方法。)
我是不是漏掉了什么?
指针很容易复制。即使您限制为 r 值引用,您仍然可以轻松制作副本(例如当您将指针作为函数参数传递时),这将使安全设置无效。此外,您将 运行 陷入模板中的问题,在模板中您可以轻松地将 T* const
或 T*&
作为类型,并且类型不匹配。
因此,您提议在没有显着提高安全性的情况下制定更多限制,这可能是它最初不在标准中的原因。
make_shared
的重点是原子化共享指针的构造。假设您有 f(shared_ptr<int>(new int(5)), throw_some_exception())
。标准不保证参数调用的顺序。允许编译器创建一个新的 int,运行 throw_some_exception
,然后构造 shared_ptr,这意味着您可以泄漏 int(如果 throw_some_exception
实际上抛出异常)。 make_shared
只是在自身内部创建对象和共享指针,不允许编译器更改顺序,因此很安全。
在很多情况下您可能无法调用 make_shared()
。例如,您的代码可能不负责分配和构造有问题的 class。出于各种原因,某些 C++ 代码库中使用了以下范例(私有构造函数 + 工厂函数):
struct A {
private:
A();
};
A* A_factory();
在这种情况下,如果您想将从 A_factory()
获得的 A*
粘贴到 shared_ptr<>
中,则必须使用采用原始指针的构造函数make_shared()
.
其他一些例子:
- 您想使用
posix_memalign()
为您的类型获取对齐内存,然后使用调用 free()
的自定义删除器将其存储在 shared_ptr<>
中(此用例很快就会消失当我们向语言添加对齐分配时!)。
- 您想将指向使用
mmap()
创建的内存映射区域的指针粘贴到带有调用 munmap()
的自定义删除器的 shared_ptr<>
中(此用例将在我们得到了 shmem 的标准化设施,我希望在接下来的几个月里继续努力。
- 您想使用自定义删除器将由分配的指针粘贴到
shared_ptr<>
中。
我对 shared_ptr
的设计没有任何特别的见解,但我认为最可能的解释是所涉及的时间表使这不可能:
shared_ptr
在 C++11 中与右值引用同时引入。 shared_ptr
在 boost
中已经有一个有效的参考实现,因此可以预期它会相对快速地添加到标准库中。
如果 shared_ptr
的构造函数仅支持从右值引用构造,它将无法使用,直到编译器 也 实现了对右值引用的支持。
那时,编译器和标准的开发更加异步,因此可能需要 年 才能实现所有编译器的支持(如果有的话)。 (导出模板在 2011 年人们记忆犹新)
此外,我认为标准委员会会对没有参考实现的 API 进行标准化感到不自在,甚至在标准发布后才能获得参考实现。
我正在阅读 Top 10 dumb mistakes to avoid with C++11 smart pointer。 第 5 号显示:
Mistake # 5 : Not assigning an object(raw pointer) to a shared_ptr as soon as it is created !
int main()
{
Aircraft* myAircraft = new Aircraft("F-16");
shared_ptr<aircraft> pAircraft(myAircraft);
...
shared_ptr<aircraft> p2(myAircraft);
// will do a double delete and possibly crash
}
推荐如下:
Use
make_shared
ornew
and immediately construct the pointer with it.
好的,毫无疑问,问题和建议。
但是我对 shared_ptr
的 design 有疑问。
这是一个很容易犯的错误,shared_ptr
的整个“安全”设计可能会被非常容易检测到的误用所抛弃。
现在的问题是,是否可以通过 shared_ptr
的替代设计轻松解决此问题,其中来自原始指针的唯一构造函数是来自右值引用的构造函数?
template<class T>
struct shared_ptr{
shared_ptr(T*&& t){...basically current implementation...}
shared_ptr(T* t) = delete; // this is to...
shared_ptr(T* const& t) = delete; // ... illustrate the point.
shared_ptr(T*& t) = delete;
...
};
这样 shared_ptr
只能从 new
或一些工厂函数的结果中初始化。
这是库中 C++ 语言的未充分利用吗? 或 如果这很可能是误用,那么从原始指针(左值)引用获取构造函数有什么意义?
这是历史事故吗? (例如 shared_ptr 是在引入 r 值引用之前提出的,等等)向后兼容?
(当然有人会说 std::shared_ptr<type>(std::move(ptr));
这更容易捕捉,并且如果确实有必要的话也是一种解决方法。)
我是不是漏掉了什么?
指针很容易复制。即使您限制为 r 值引用,您仍然可以轻松制作副本(例如当您将指针作为函数参数传递时),这将使安全设置无效。此外,您将 运行 陷入模板中的问题,在模板中您可以轻松地将 T* const
或 T*&
作为类型,并且类型不匹配。
因此,您提议在没有显着提高安全性的情况下制定更多限制,这可能是它最初不在标准中的原因。
make_shared
的重点是原子化共享指针的构造。假设您有 f(shared_ptr<int>(new int(5)), throw_some_exception())
。标准不保证参数调用的顺序。允许编译器创建一个新的 int,运行 throw_some_exception
,然后构造 shared_ptr,这意味着您可以泄漏 int(如果 throw_some_exception
实际上抛出异常)。 make_shared
只是在自身内部创建对象和共享指针,不允许编译器更改顺序,因此很安全。
在很多情况下您可能无法调用 make_shared()
。例如,您的代码可能不负责分配和构造有问题的 class。出于各种原因,某些 C++ 代码库中使用了以下范例(私有构造函数 + 工厂函数):
struct A {
private:
A();
};
A* A_factory();
在这种情况下,如果您想将从 A_factory()
获得的 A*
粘贴到 shared_ptr<>
中,则必须使用采用原始指针的构造函数make_shared()
.
其他一些例子:
- 您想使用
posix_memalign()
为您的类型获取对齐内存,然后使用调用free()
的自定义删除器将其存储在shared_ptr<>
中(此用例很快就会消失当我们向语言添加对齐分配时!)。 - 您想将指向使用
mmap()
创建的内存映射区域的指针粘贴到带有调用munmap()
的自定义删除器的shared_ptr<>
中(此用例将在我们得到了 shmem 的标准化设施,我希望在接下来的几个月里继续努力。 - 您想使用自定义删除器将由分配的指针粘贴到
shared_ptr<>
中。
我对 shared_ptr
的设计没有任何特别的见解,但我认为最可能的解释是所涉及的时间表使这不可能:
shared_ptr
在 C++11 中与右值引用同时引入。 shared_ptr
在 boost
中已经有一个有效的参考实现,因此可以预期它会相对快速地添加到标准库中。
如果 shared_ptr
的构造函数仅支持从右值引用构造,它将无法使用,直到编译器 也 实现了对右值引用的支持。
那时,编译器和标准的开发更加异步,因此可能需要 年 才能实现所有编译器的支持(如果有的话)。 (导出模板在 2011 年人们记忆犹新)
此外,我认为标准委员会会对没有参考实现的 API 进行标准化感到不自在,甚至在标准发布后才能获得参考实现。