static_cast转换至动态类型非基类的运行原理及代码疑问解析
C++ static_cast 工作机制与代码行为解析
先看你提供的代码及输出:
代码
#include <iostream> using namespace std; class A { public: virtual void f() const { cout << " A::f "; } virtual void g() { cout << " A::g "; } virtual A* n() { cout << " A::n "; return this; } virtual void t() { cout << " A::t "; } }; class B: public A { public: virtual void f() const { cout << " B::f "; } void g() { cout << " B::g "; A::n(); } A* n() { cout << " B::n "; return this; } void t() { cout << " B::t "; } }; class C: public A { public: virtual void f() { cout << " C::f "; } void g() const { cout << " C::g "; } }; int main() { A* q2 = new B(); A* q3 = new C(); (static_cast<C*>(q2))->g(); cout << endl; (static_cast<C*>(q2))->t(); cout << endl; (static_cast<B*>(q3->n()))->f(); cout << endl; return 0; }
输出
C::g B::t A::n A::f
下面逐个分析关键语句的行为:
1. (static_cast<C*>(q2))->g();
q2是指向B对象的A*指针,static_cast<C*>(q2)属于非法的交叉转换:B和C是A的平行派生类,无继承关系,这种转换本身是未定义行为(C++标准不保证任何结果)。- 输出
C::g的原因:A的g()签名是void g(),而C的g()是void g() const,两者签名不同,不属于虚函数重写,只是同名重载。当通过C*指针调用g()时,编译器会进行静态绑定,直接生成调用C::g()的机器码,完全忽略指针实际指向的是B对象。这种结果是不可靠的,换个编译器或环境可能有不同表现。
2. (static_cast<C*>(q2))->t();
t()是A中声明的虚函数,B重写了它,C没有重写(继承自A的t())。- 虚函数的调用是动态绑定,只看指针实际指向的对象类型,和指针被强制转换后的类型无关。这里
q2实际指向B对象,所以调用B::t(),输出B::t。 - 注意:虽然这里转换本身还是未定义行为,但虚函数的动态绑定机制让结果看起来符合预期,本质上仍是危险的操作。
3. (static_cast<B*>(q3->n()))->f();
- 第一步:
q3是指向C对象的A*指针,调用n()。C没有重写A的n(),所以调用A::n(),输出A::n,并返回指向C对象的A*指针。 - 第二步:
static_cast<B*>(...)又是非法的交叉转换(C对象转成B*),属于未定义行为。 - 第三步:调用
f()。A的f()是virtual void f() const,B重写了该签名的函数,而C的f()是void f()(签名不同,不属于重写)。因此C对象的虚函数表中,f() const对应的仍是A::f()。动态绑定后,调用A::f(),输出A::f。
关键总结
static_cast是编译期转换,不会做运行时类型检查:只要编译器认为类型转换语法合法(比如派生类和基类之间的转换),就会生成转换代码,完全不管实际对象的真实类型。- 当转换涉及无继承关系的平行类时,属于未定义行为,结果不可预测。
- 虚函数的调用由实际对象的类型决定,和指针的静态类型无关;非虚函数则由指针的静态类型决定,直接绑定到对应类的函数。
内容的提问来源于stack exchange,提问作者matteo camillo
相关产品推荐
相关产品推荐

