我有以下代码片段
class base
{
public:
virtual void MyMethod()
{
std::cout << "Base MyMethod" << "\n";
}
};
class der : public base
{
public:
virtual void MyMethod()
{
std::cout << "Derived MyMethod" << "\n";
}
};
void foo(base* b)
{
der* d = static_cast<der*>(b);
d->MyMethod();
}
int main()
{
base* b = new der();
foo(b);
}
现在我的问题是为什么 static_Cast 在这里工作。我读到 static_casts 不能通过多态类型进行转换。那么为什么上面的例子有效 - 我在这里遗漏了什么吗?我期望动态转换只能解决这样的问题,因为它们应该与多态类型一起使用?谁能举一个静态强制转换会失败而动态强制转换会通过的例子?
“现在我的问题是为什么
在这里工作。”static_cast
没有理由说它不起作用。这些类型通过类派生相关,这是编译器已知的。本质上
static_cast
仅限于执行或撤消任何隐式转换,并且您确实有从 der*
到 base*
的隐式转换。
“我读到
不能通过多态类型进行转换。”static_cast
这只是胡言乱语。
“[剪断]谁能举个例子,其中
会失败而static cast
会通过?”dynamic cast
struct A { virtual ~A(){} };
struct B { virtual ~B(){} };
struct T: A, B {};
auto main()
-> int
{
T o;
A& oA = o;
//B& oB = static_cast<B&>( oA ); //! This won't compile, unrelated types.
B& oB = dynamic_cast<B&>( oA );
}
通常使用dynamic_cast,将基指针转换为派生指针。这是因为基类指向的对象实际上可能不是派生类型。因此,dynamic_cast 执行运行时检查,如果对象不兼容,则返回空指针。
但是这种运行时检查会产生轻微的性能成本。如果您完全确定程序的逻辑,转换会成功,则可以使用 static_cast 来代替并阻止运行时检查。但如果你的对象类型错误,你将得到未定义的行为。
对于继承,概念上存在三种转换:
X
到类型Y
与基础派生没有直接关系,但可能有一个类型T
派生了它们两者。如果基类非
static_cast
或唯一,则向下转型可以是virtual
。
struct Base { virtual ~Base() = default; };
struct A : virtual Base { };
struct B : virtual Base { };
struct T : A, B { };
struct T2 : A { };
这里,
A
和B
实际上继承自Base
,这意味着T
只有一个Base
子对象,而不是两个,并且从T
到Base
的继承路径并不唯一,因为它可以穿过 A
或 B
。另一方面,对于 T2
,它仅贯穿 A
,因此是唯一的。
A
static_cast
始终只是指针调整。因此,向下转型是不安全的,但当可以在编译时确定指针偏移量时,向下转型是可能的;当继承是非virtual
时,例如从A
到T
/T2
或B
到T
,但不是从Base
到任何东西,这是可能的。对于向下转型,需要对象的特定运行时类型知道其确切的布局,然后根据它进行指针调整。
int main()
{
T t{};
A& a = t; // upward, requires no explicit cast
B& b = t; // upward, requires no explicit cast
//T& t2 = a; // error, downcast
T& t3 = static_cast<T&>(a); // good, because `a` actually is a `T`
T& t4 = static_cast<T&>(b); // good, because `b` actually is a `T`
//B b2 = a; // error: sideways cast
//B b3 = static_cast<B&>(a); // error: sideways cast
B b4 = dynamic_cast<B&>(a); // okay, throws if `a` isn’t actually a `B`
Base& base = t;
//T& t5 = base; // error: downcast
//T& t6 = static_cast<T&>(base); // error: virtual downcast
T& t7 = dynamic_cast<T&>(base); // good, throws if `base` isn’t actually a `T`
// A& a2 = static_cast<T2&>(base); // error: virtual downcast
A& a3 = dynamic_cast<A&>(base); // good, throws if `base` isn’t actually an `A`
T2 tt{};
Base& base2 = tt;
T2& tt2 = static_cast<T2&>(tt);
}