std::unique_ptr<T[]> API 禁止派生指针到基指针的转换
std::unique_ptr<T[]> API prohibits derived-to-base pointer conversions
在 Modern Effective C++, "Iterm 19: Use std::shared_ptr
for shared-ownership resource management.", 第 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.)
"open holes in the type system"是什么意思?
为什么 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 |
=================================
数组中的前两个对象会将第 2 个和第 4 个 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
成员。并且数组的一半丢失了。
现在想象一下删除这样一个强制转换的数组。将调用哪些析构函数?什么内存将被释放?此时你已经深陷地狱了。
等等,还有更多。
假设你有 3 个 类 这样的:
class A
{
char i;
};
class B1 : public A
{
float j;
};
class B2 : public A
{
int k;
};
现在您创建一个 B1
指针数组:
B1* b1[4];
如果将该数组转换为 A
个指针数组,您可能会想,"well this is fine, right"?
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.
所以你又一次陷入了深渊,编译器将无法警告你,除非一开始就不允许向上转换。
为什么 std::unique_ptr API 禁止派生指针到基指针的转换?
我希望以上解释能给出充分的理由。
怎么会禁止转换呢?
它根本不提供任何 API 进行转换。 shared_ptrAPI有static_pointer_cast
的转换功能,unique_ptrAPI没有
在 Modern Effective C++, "Iterm 19: Use std::shared_ptr
for shared-ownership resource management.", 第 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.)
"open holes in the type system"是什么意思?
为什么 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 |
=================================
数组中的前两个对象会将第 2 个和第 4 个 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
成员。并且数组的一半丢失了。
现在想象一下删除这样一个强制转换的数组。将调用哪些析构函数?什么内存将被释放?此时你已经深陷地狱了。
等等,还有更多。
假设你有 3 个 类 这样的:
class A
{
char i;
};
class B1 : public A
{
float j;
};
class B2 : public A
{
int k;
};
现在您创建一个 B1
指针数组:
B1* b1[4];
如果将该数组转换为 A
个指针数组,您可能会想,"well this is fine, right"?
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.
所以你又一次陷入了深渊,编译器将无法警告你,除非一开始就不允许向上转换。
为什么 std::unique_ptr API 禁止派生指针到基指针的转换?
我希望以上解释能给出充分的理由。
怎么会禁止转换呢?
它根本不提供任何 API 进行转换。 shared_ptrAPI有static_pointer_cast
的转换功能,unique_ptrAPI没有