我很确定我理解上投和下投之间的一般区别,尤其是在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
如果基类不明确(通过不同的路径继承了两次或多次),则无法在单个步骤中进行向上转换。
如果基类不可访问,则向上转换的唯一方法是使用 C 样式转换。这是该演员的一个特例,它是唯一可以完成这项工作的人。从本质上讲,它表现为不受可访问性限制的static_cast
。
标准语。
C++11 §5.4/4:
"...在[C cast]中执行
static_cast
在以下情况下转换 即使基类不可访问,也有效:指向派生类类型的
- 对象的指针或派生类类型的左值或右值可以显式 分别转换为指针或对明确基类类型的引用;
指向- 派生类类型成员的指针可以显式转换为指向 明确的非虚拟基类类型;
- 指向明确非虚拟基类类型的对象的指针,明确基类类型的 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;
};
在两种情况下,上行转换在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; }