关于C++:当一个子类的指针传递给函数时,为什么一个类被引用而不是它的子类呢?

Why does a class get referenced instead of its subclass when a pointer to that subclass is passed to a function?

本问题已经有最佳答案,请猛点这里访问。

考虑到一个类"a",它的子类"b"和一个函数"f"。函数"f"调用其单个参数"a*obj"的方法"p",它是指向"a"对象的指针。现在,在创建类"b"的对象"b"并将其指针传递给"f"之后,即使"b"是"b"对象,也会引用"a"而不是"b"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

class A {
public:
    void p() {
        std::cout <<"boring...";
    }
};

class B : public A {
public:
    void p() {
        std::cout <<"woo!";
    }
};

void f(A * obj) {
    obj->p();
}

int main(int argc, char **argv) {
    B b;
    f(&b);
    return 0;
}

输出:

1
boring...

为什么会这样?


C++语言的基本设计规则之一是"你不为你不使用的东西付费"。通过指向基类型的指针调用派生类中定义的函数有点困难,所以规则是必须告诉编译器您打算这样做。因此,在所讨论的代码中,A* obj = whatever; a->p();调用A::p(也就是在类A中定义的p的版本)。而B* obj = whatever; b->p();B::p。为了使a->p()调用B中的版本,您必须告诉编译器您要这样做,并通过将A::p标记为virtual来做到这一点:

1
2
3
4
5
class A {
public:
    virtual void p() {
    }
};

为了调用动态调度(即调用运行时类型的函数),必须将函数标记为virtual

1
2
3
4
5
6
class A {
public:
    virtual void p() {
        std::cout <<"boring...";
    }
};

这是因为C++的哲学:你不为你不需要的东西付出代价。动态调度也不是免费的,因为您需要从对象中查找正确的函数指针。


C++中的虚拟调度不是自动的。你的对象可能是BA,但f()只知道你给它一个指向A的指针。它不知道B部分也存在。

您可以使用关键字virtual来获得这种行为,并且需要一些运行时性能开销。

请参阅C++关于多态性的书中的章节以获得更多信息。