在Modern Effective C++中,“Iterm 19:使用 std::shared_ptr
进行共享所有权资源管理。”,第 133-134 页,它说:
std::shared_ptr supports derived-to-base pointer conversions that make sense for single objects, but that open holes in the type system when applied to arrays. (For this reason, the std::unique_ptr API prohibits such conversions.)
“类型系统中的漏洞”是什么意思?
为什么std::unique_ptr<T[]>
API 禁止派生到基址的指针转换?
它怎么能禁止转换?
最佳答案
类型系统中的漏洞是指当一个类型被强制转换为另一个不兼容的类型时编译器没有捕捉到。
假设你有两个简单的类:
class A
{
char i;
};
class B : public A
{
char j;
};
为简单起见,让我们忽略填充等内容,并假设 A
类型的对象为 1 个字节,B
类型的对象为 2 个字节。
现在,当您有一个 A
类型的数组或 B
类型的数组时,它们将如下所示:
A a[4]:
=================
| 0 | 1 | 2 | 3 |
|-------|-------|
| i | i | i | i |
=================
B b[4]:
=================================
| 0 | 1 | 2 | 3 |
|-------|-------|-------|-------|
| i | j | i | j | i | j | i | j |
=================================
现在假设您有指向这些数组的指针,然后将一个转换为另一个,这显然会导致问题:
a cast to B[4]:
=================================
| 0 | 1 | 2 | 3 |
|-------|-------|-------|-------|
| i | j | i | j | x | x | x | x |
=================================
数组中的前两个对象会将第二个和第四个 A
的 i
成员解释为它们的 j
成员。第二个和第三个成员访问未分配的内存。
b cast to A[4]:
=================
| 0 | 1 | 2 | 3 |
|-------|-------|
| i | i | i | i | x | x | x | x |
=================
反过来,所有 4 个对象现在交替将 2 个 B
实例的 i
和 j
解释为它们的 i
成员。并且数组丢失了一半。
现在想象一下删除这样一个强制转换的数组。将调用哪些析构函数?什么内存会被释放?此时你正处于 hell 般的深渊。
但等等,还有更多。
假设你有 3 个这样的类:
class A
{
char i;
};
class B1 : public A
{
float j;
};
class B2 : public A
{
int k;
};
现在你创建了一个 B1
指针数组:
B1* b1[4];
如果您将该数组转换为 A
指针数组,您可能会想,“这很好,对吧”?
A** a = <evil_cast_shenanigans>(b1);
我的意思是,您可以安全地访问每个成员作为指向 A
的指针:
char foo = a[0]->i; // This is valid
但你也可以这样做,是这样的:
a[0] = new B2{}; // Uh, oh.
这是一个有效的赋值,没有编译器会提示,但你不能忘记我们实际上是在处理一个数组,它是作为指向 B1
对象的指针数组创建的。它的第一个成员现在指向 B2
对象,您现在可以作为 B1
访问该对象,而无需编译器说什么。
float bar = b1[0]->j; // Ouch.
所以你又一次陷入了 hell ,编译器将无法警告你,除非首先不允许向上转换。
为什么 std::unique_ptr API 会禁止派生到基址的指针转换?
我希望以上解释能给出充分的理由。
它如何禁止转换?
它根本不提供任何 API 来进行转换。 shared_ptr API 有 static_pointer_cast
之类的转换函数,unique_ptr API 没有。
关于c++ - std::unique_ptr<T[]> API 禁止派生到基指针的转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52050530/