c++多态 virtual 引用传递和值传递
#include <iostream>
using namespace std;
class A
{
public:
virtual void f()
{
cout << 1;
}
void g()
{
cout << 2;
}
};
class B : public A
{
public:
virtual void f()
{
cout << 3;
}
void g()
{
cout << 4;
}
};
void show(A &a)
{
a.f();
a.g();
}
void show2(A a){
a.f();
a.g();
}
int main()
{
B b;
show(b);
cout<<endl;
show2(b);
cout<<endl;
system("pause");
return 0;
}
在回答之前,先梳理一下关键的 C++ 机制和假设:
-
虚函数和多态
-
只要你通过基类指针或引用调用一个
virtual方法,运行时就会根据对象的 实际类型(dynamic type)去调用对应的重写版本。 -
非虚函数总是根据 静态类型(static type,也就是写在代码里的类型)调用。
-
-
传值时的对象切片(slicing)
-
当你把一个派生类对象传给接受基类 值 的函数时,函数内部会拷贝出一个 纯基类子对象,原来的派生部分就被“切掉”了(切片)。
-
这个拷贝出来的对象,其虚函数表指针指向的是 基类,因此再调用虚函数就只能跑基类的版本。
-
void show(A &a) {
a.f(); // virtual 调用
a.g(); // 非 virtual 调用
}
void show2(A a) {
a.f(); // virtual 调用,但‘a’已经是纯 A 了
a.g(); // 非 virtual 调用
}
int main() {
B b;
show(b);
cout << endl;
show2(b);
cout << endl;
return 0;
}
-
b的类型是B。 -
show接受A&(引用),不会发生切片; -
show2接受A(值),会发生切片。
1. 调用 show(b);
-
形参
a是对b的别名——引用,实际保留了派生类型信息。 -
a.f():f是虚函数,运行时看a真正指向的是B,调用B::f(),输出3。 -
a.g():g不是虚函数,静态类型是A,调用A::g(),输出2。
最终:show(b) 打印32
2. 调用 show2(b);
-
形参
a拷贝了b中的 A 子对象,派生对象的B部分被切掉。 -
拷贝后的
a的动态类型、静态类型都是A。 -
a.f():虽然f在A中是virtual,但是a的 vtable 已经指向A,调用A::f(),输出1。 -
a.g():非虚,直接调用A::g(),输出2。
最终:show2(b) 打印12
总结
| 调用 | f() (virtual) |
g() (non-virtual) |
原因 |
|---|---|---|---|
show(b) |
B::f() → 3 |
A::g() → 2 |
引用保持动态类型,多态 vs 静态 |
show2(b) |
A::f() → 1 |
A::g() → 2 |
值传递切片,只剩基类子对象 |

浙公网安备 33010602011771号