为什么静态向下转换unique_ptr不安全?
Why is static downcasting unique_ptr unsafe?
我指的是"Downcasting"unique_ptr
OP 要求从unique_ptr<Base>
中获取unique_ptr<Derived>
,其中后一个对象是动态类型Derived
,因此静态向下转换是安全的。
通常,(正如互联网上95%的解决方案所建议的那样,粗略估计(,简单的解决方案是:
unique_ptr<Derived> ptr(static_cast<Derived*>(baseClassUniquePtr.release()));
OP也指出,虽然
附言。还有一个额外的复杂性,因为一些工厂 驻留在运行时动态加载的 DLL 中,这意味着 I 需要确保生成的对象在同一件作品中被销毁 创建时的上下文(堆空间(。所有权转让 (通常在另一个上下文中发生(然后必须提供 原始上下文中的删除程序。但除了必须提供/将删除器与指针一起强制转换,转换问题应该是 一样。
现在,解决方案似乎是从unique_ptr<Base>
对象中获取删除器并将其传递给新对象,这显然会导致unique_ptr<Derived, default_delete<Base>>
。但无论如何,default_delete
是无国籍的。唯一的区别是模板参数。但是由于我们在C++中使用具有动态多态性的继承时总是声明dtor
virtual
,因此无论如何我们总是调用~Derived
,所以我认为,原始删除器无论如何都是安全的,允许干净的强制转换unique_ptr<Derived>
(没有不方便的第二个模板参数,禁止任何通常的存储(。
因此,虽然我知道在使用库(DLL,.dylib,...(创建对象并将其传递给某个可执行文件时,我们有两个堆空间,但我不明白从旧对象复制/移动无状态删除器如何解决此问题。
它甚至能解决问题吗?如果是,如何?如果没有,我们如何解决这个问题?
---编辑:还...get_deleter
返回对位于旧unique_ptr
中的对象的引用,当这个旧unique_ptr
被销毁时,该对象被销毁,不是吗?---(愚蠢的问题,因为unique_ptr
与删除器一起明显被移动(
它甚至解决了这个问题吗?
你是对的,它没有(靠自己(。但这并不是因为默认删除程序是无状态的,而是因为它的实现是内联的。
如果没有,我们如何解决这个问题?
我们必须确保对delete
的调用发生在最初分配对象的模块中(我们称之为模块 A(。由于std::default_delete
是一个模板,因此它是按需实例化的,并且从模块 B 调用内联版本
方法 1
一种解决方案是一直使用自定义删除器。它不必是有状态的,只要它的实现驻留在模块 A 中。
// ModuleA/ModuleADeleter.h
template <class T>
struct ModuleADeleter {
// Not defined here to prevent accidental implicit instantiation from the outside
void operator()(T const *object) const;
};
// Suppose BUILDING_MODULE_A is defined when compiling module A
#ifdef BUILDING_MODULE_A
#define MODULE_A_EXPORT __declspec(dllexport)
#else
#define MODULE_A_EXPORT __declspec(dllimport)
#endif
template class MODULE_A_EXPORT ModuleADeleter<Base>;
template class MODULE_A_EXPORT ModuleADeleter<Derived>;
// ModuleA/ModuleADeleter.cpp
#include "ModuleA/ModuleADeleter.h"
template <class T>
void ModuleADeleter<T>::operator()(T const *object) const {
delete object;
}
template class ModuleADeleter<Base>;
template class ModuleADeleter<Derived>;
(此处介绍了从 DLL 导入/导出模板实例化(。
此时,我们只需要从模块 A 返回std::unique_ptr<Base, ModuleADeleter<Base>>
,并根据需要始终转换为std::unique_ptr<Derived, ModuleADeleter<Derived>>
。
请注意,仅当Base
具有非虚拟析构函数时才需要ModuleADeleter<Derived>
,否则只需重用ModuleADeleter<Base>
(如链接答案(即可按预期工作。
方法 2
最简单的解决方案是使用std::shared_ptr
而不是std::unique_ptr
.它有一些性能损失,但您不需要实现和更新删除器,或手动转换它。这是有效的,因为std::shared_ptr
在构造时实例化并类型擦除其删除器,这是在模块 A 中完成的。然后,此删除器将被存储并保留,直到需要为止,并且不会出现在指针的类型中,因此您可以自由混合指向从各种模块实例化的对象的指针。
也。。。
get_deleter
返回对位于旧unique_ptr
中的对象的引用,当这个旧unique_ptr
被销毁时,该对象被销毁,不是吗?
否,get_deleter
的返回值是指您调用它的unique_ptr
中包含的删除程序。在unique_ptr
s 之间移动时,删除程序状态的传输方式在此处的重载 #6 中进行了描述。
- C++/CLI 和 C#/VB 与不安全和外部有什么区别?
- 问:Apache Arrow 数组生成器不安全追加
- 不安全的 MPI 非阻塞通信示例?
- 有没有一种简单的方法来检查C++中的不安全表达式
- 为什么静态向下转换unique_ptr不安全?
- 哪些整数操作不安全
- 为什么这个递归 lambda 函数不安全?
- 解决方法:QPixmap:在GUI线程之外使用pixmap是不安全的
- 正在匹配不安全的正则线程
- 如何修复编译错误"此函数或变量可能不安全"(strcpy)
- 编译器在 const ref 类型参数上使用临时对象时是否应该警告不安全的行为?
- 实现没有不安全服务器凭据的自定义 AuthMetadataProcessor
- 什么时候关闭__strict_ansi__标志是不安全的
- 为什么(常量字符*)ptr 不被视为左值
- 原子对象在普通对象安全的任何上下文中都是不安全的
- OpenSSL:将不安全的BIO提升为安全
- 这是对支撑初始器列表的不安全使用情况
- 从 C# 到C++和返回的数组,没有不安全的代码
- 一个线程设置成员,而另一个循环上方 - 是此螺纹 - 不安全
- 访问"std::variant"的不安全、"noexcept"和无开销方式