static_cast - 为什么它在这里工作?

问题描述 投票:0回答:3

我有以下代码片段

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 不能通过多态类型进行转换。那么为什么上面的例子有效 - 我在这里遗漏了什么吗?我期望动态转换只能解决这样的问题,因为它们应该与多态类型一起使用?谁能举一个静态强制转换会失败而动态强制转换会通过的例子?

c++ static-cast
3个回答
6
投票

“现在我的问题是为什么

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 );
}

3
投票

通常使用dynamic_cast,将基指针转换为派生指针。这是因为基类指向的对象实际上可能不是派生类型。因此,dynamic_cast 执行运行时检查,如果对象不兼容,则返回空指针。

但是这种运行时检查会产生轻微的性能成本。如果您完全确定程序的逻辑,转换会成功,则可以使用 static_cast 来代替并阻止运行时检查。但如果你的对象类型错误,你将得到未定义的行为。


0
投票

对于继承,概念上存在三种转换:

  • 向上:从派生类到基类,
  • 向下:从基类到派生类,
  • sideways:从类型
    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);
}
© www.soinside.com 2019 - 2024. All rights reserved.