我相信我清楚地理解了在C++中上转换和下转换之间的一般区别。 我知道我们不能总是下转换,因为将基类指针转换为派生类指针将假定被指向的基类对象具有派生类的所有成员。
在这学期早期,我的教授告诉我们有时在C++中进行上转换也是不合法的,但是我在笔记中错过了原因,并且我记不得发生这种情况的时间。
在C++中什么时候上转换是不合法的?
我相信我清楚地理解了在C++中上转换和下转换之间的一般区别。 我知道我们不能总是下转换,因为将基类指针转换为派生类指针将假定被指向的基类对象具有派生类的所有成员。
在这学期早期,我的教授告诉我们有时在C++中进行上转换也是不合法的,但是我在笔记中错过了原因,并且我记不得发生这种情况的时间。
在C++中什么时候上转换是不合法的?
当基类为私有时,就会无法访问。
class A {};
class B : A {};
...
B b;
A *pa = &b; // ERROR: base class is inaccessible
请注意,即使在C++11中,C风格的转换也可以“突破”访问保护,执行正式正确的向上转型。
A *pa = (A *) &b; // OK, not a `reinterpret_cast`, but a valid upcast
当然,应该避免使用这种用法。
如果您的源类型包含目标类型的多个基础子对象(通过多重继承),则会产生歧义。
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
D d;
A *pa = &d; // ERROR: base class is ambiguous
在这种情况下,可以通过显式地“沿着”所需的向上转型路径进行中间向上转型来执行向上转型,直到基类不再模糊为止。
在这种情况下,可以通过显式地“沿着”所需的向上转型路径进行中间向上转型来执行向上转型,直到基类不再模糊为止。
B* pb = &d;
A* pa = pb; // OK: points to 'D::B::A' subobject
reinterpret_cast
(这可能会导致潜在且几乎无法检测到的错误,如果基类访问发生变化),要么将其设为破坏保护的static_cast
(他们选择了后者)。这两种变体都很糟糕。 - AnT stands with Russia如果基类存在二义性(通过不同的路径继承了两次或更多次),那么你不能在单一步骤中进行向上转换。
如果基类是不可访问的,则仅能使用C样式转换进行向上转换。这是该转换的一个特殊情况,它是唯一可以胜任此项工作的转换类型。实质上,它的行为类似于未受可访问性限制的static_cast
。
C++11 §5.4/4标准:
” … 对于以下情况,在[a C cast]执行
static_cast
时即使基类是不可访问的,转换也是有效的:
- 派生类类型的对象指针或派生类类型的lvalue或rvalue可以分别显式转换为非二义性的基类类型的指针或引用;
- 派生类类型的成员指针可以显式转换为非虚拟基类类型的成员指针;
- 非二义性非虚拟基类类型的对象指针、非二义性非虚拟基类类型的glvalue或非二义性非虚拟基类类型的成员指针可以分别显式转换为派生类类型的指针、引用或成员指针。
二义性示例:
struct Base {};
struct M1: Base {};
struct M2: Base {};
struct Derived: M1, M2 {};
auto main() -> int
{
Derived d;
//static_cast<Base&>( d ); //! Ambiguous
static_cast<Base&>( static_cast<M2&>( d ) ); // OK
}
无法访问的基础示例,通常需要在转换中进行地址调整:
struct Base { int value; Base( int x ): value( x ) {} };
class Derived
: private Base
{
public:
virtual ~Derived() {} // Just to involve an address adjustment.
Derived(): Base( 42 ) {}
};
#include <iostream>
using namespace std;
auto main() -> int
{
Derived d;
Base& b = (Base&) d;
cout << "Derived at " << &d << ", base at " << &b << endl;
cout << b.value << endl;
};
const_cast
。(2) 如果指定类型既不是基类也不是派生类或同一类,则会愉快地执行reinterpret_cast
(可能还包括添加的const_cast
),这是通往灾难的一步。不确定是否应该包含这些细节。 - Cheers and hth. - Alfauto main() -> int
而不是只写 int main()
呢?我不明白为什么在返回类型已经明确的情况下使用自动类型推断语法(而且不依赖于任何函数参数的 decltype
)。只是想理解一下是否有一些微妙的见解你拥有而我没有注意到的。 - Cornstalksauto
语法的同时使用C++11之前的函数声明语法。无论如何,都必须使用auto
语法。除了(1)成为函数声明的单一通用语法外,它还(2)允许您在每次查看代码时在同一位置看到函数名称,并且(3)在返回类型规范中使用未限定类型,适用于成员函数。尽管如此,我仍然将main
作为特例,在同一行上编写返回类型,并使用旧语法声明void
函数,类似于Pascal的procedure
。 - Cheers and hth. - Alf在C++中,向上转型存在两种情况会被视为不合法(在编译时会被诊断):
涉及的基类不可访问:
class base {};
class derived : base {};
int main() {
derived x;
base& y = x; // invalid because not accessible.
// Solution: C-style cast (as static_cast without access-check)
base& y1 = (base&)x;
}
问题中的基类子对象不是明确的:
class base {};
struct A1 : base {};
struct A2 : base {};
struct derived : A1, A2 {};
int main() {
derived x;
base& y = x; // invalid because ambiguous.
// Solution 1, scope resolution:
base& y1 = static_cast<A1::base&>(x);
base& y2 = static_cast<A2::base&>(x);
// Solution 2, intermediate unambiguous steps:
A1& a1 = x;
A2& a2 = x;
base& ya1 = a1;
base& ya2 = a2;
}