为什么显式类型转换允许私有继承的向上转型?

25
#include<iostream>
using namespace std;

class A {
    public:
    void f(){cout<<"A"<<endl;}
};

class B : private A {
    public:
    void f(){cout<<"B"<<endl;}
};

int main (){

由于Class B私有继承Class A,因此这种向上转型不应该起作用:

    A* a = new B;

但是显式类型转换是允许的。 为什么呢?

    A* a1 = (A*)new B;
    a1->f();
    return 0;
}

1
C-cast 允许大多数转换(甚至从 int*A*)。然后使用可能会导致未定义行为,输出也可能不符合预期。 - Jarod42
重复:https://dev59.com/HmMm5IYBdhLWcg3wDbin - M.M
4个回答

19

在代码中的强制类型转换

A* a1 = (A*)new B;

这是对不可访问基类的强制类型转换。

只能用 C 风格的强制类型转换来表示。如果可以在此情况下使用 static_cast,则等效于 static_cast 所做的操作,但它不等同于 reinterpret_cast。特别地,结果地址未必与参数地址相同。

C++11 §5.4/4:

对于 C 风格的强制类型转换,具有与 static_cast 相同的语义限制和行为 [除了以下情况外]:

— 派生类类型对象的指针或派生类类型的左值或右值引用可以显式转换为一个明确的基类类型的指针或引用;


2
如果不能用reinterpret_cast和const_cast来表达,那么这根本不是一个有效的转换。 - Richard Hodges
5
@RichardHodges说:对于无法访问的基类,以及对于一般情况下的dynamic_cast和甚至static_cast,都不能用reinterpret_castconst_cast表示。也就是说,你的“如果不能用reinterpret_cast和const_cast表示,那么这就不是一个有效的转换”这种说法是无意义的。抱歉。 - Cheers and hth. - Alf
1
我看到了参考资料并纠正了错误。谢谢。 - Richard Hodges

8
这是 C 风格转换规则允许的:

(N3337) [expr.cast]/4: 可以使用显式类型转换符号执行以下转换:

  • a const_cast (5.2.11),
  • a static_cast (5.2.9),
  • a static_cast followed by a const_cast,
  • a reinterpret_cast (5.2.10), 或者
  • a reinterpret_cast followed by a const_cast。

相同的语义限制和行为适用,唯一的例外是在以下情况下进行 static_cast 转换时,即使基类不可访问也是有效的:

  • 派生类类型的对象指针或派生类类型的 lvalue 或 rvalue 可以分别显式转换为无歧义的基类类型的指针或引用;

  • [...]

如果您更改为 static_cast,则编译将失败。GCC 给出此错误提示:
error: 'A' is an inaccessible base of 'B'
    A* a1 = static_cast<A*>(new B);

5

显式转换允许你做任何你想做的事情。例如,你可以这样写:

int *p = (int*)new B; 

代码将被编译。

显式转换意味着您知道自己在做什么。


12
“显式转换”意味着你知道自己在做什么。=> 通常恰恰相反 :-) - HostileFork says dont trust SE
显式转换告诉编译器“我知道我在做什么”。 - Alexey Subbota
当我告诉编译器“-Wold-style-cast”时,它更加确信。 - HostileFork says dont trust SE
@Alexey Subbota - “显式转换告诉编译器:“我知道我在做什么” - 它更多地是说:我知道可能我正在做错的事情,但你应该照做。” - SChepurin

1
明确的类型转换是C语言的一个编程特性,它没有任何面向对象的特性或规则。对于C语言来说,一切都是指针(指向内存或函数)。向上转型和向下转型是由C++提供的面向对象特性。继承转换必须遵循语言的面向对象规则。然而,通过这样的类型转换:A* a1 = (A*)new B;你强制编译器将对象视为A,就像你强制将一个结构体的指针指向另一个结构体一样。因此,当编译器决定将哪个函数指针分配给转换后对象的f()调用指针时,它会分配A类实现的指针。

我认为这是最相关的答案,因为提问者询问了原因。 - Flowing Cloud

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接