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++ 机制和假设:

  1. 虚函数和多态

    • 只要你通过基类指针或引用调用一个 virtual 方法,运行时就会根据对象的 实际类型(dynamic type)去调用对应的重写版本。

    • 非虚函数总是根据 静态类型(static type,也就是写在代码里的类型)调用。

  2. 传值时的对象切片(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():虽然 fA 中是 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 值传递切片,只剩基类子对象

 

 

 

posted @ 2025-06-12 13:43  ChuckLu  阅读(21)  评论(0)    收藏  举报