当您使用基类指针`p`来调用成员函数时,对于非虚函数,编译器会根据指针的类型(在这里是`A*`)来决定调用哪个函数。对于虚函数,编译器会根据对象的实际类型(在这里是`B`对象)来决定调用哪个函数。
在这段代码中:
```cpp
A *p = &a;
p->foo(); // 输出1,因为p是A类型的指针,所以调用A::foo
p->fun(); // 输出2,因为fun是虚函数,p指向a,所以调用A::fun
p = &b;
p->foo(); // 输出1,即使p现在指向B对象b,foo是非虚函数,所以依然调用A::foo
p->fun(); // 输出4,fun是虚函数,p指向B对象b,所以调用B::fun,体现多态
```
您提到的这一行:
```cpp
p->foo(); // 取决于指针类型,输出1
```
实际上,输出是`1`,这是因为`foo`函数在`A`类中没有被声明为虚函数,所以编译器会根据指针的类型(`A*`)来解析函数调用。即使`p`指针后来被赋值为指向`B`对象,调用`foo`函数时仍然会调用`A::foo`,因为`foo`不是虚函数,编译器不会考虑对象的实际类型。
如果您希望`foo`函数在派生类`B`中被覆盖,您需要在基类`A`中将其声明为虚函数:
```cpp
class A
{
public:
virtual void foo() // 声明为虚函数
{
printf("1\n");
}
virtual void fun()
{
printf("2\n");
}
};
```
在这种情况下,当`p`指向`B`对象时,调用`foo`函数将会输出`3`,因为`B::foo`现在是`A::foo`的覆盖版本,且`foo`是虚函数,所以编译器会根据对象的实际类型解析函数调用。
注意使用指针时,是根据 指针的类型(`A*`)来解析函数调用。即使`p`指针后来被赋值为指向`B`对象,调用`foo`函数时仍然会调用`A::foo`,因为`foo`不是虚函数,编译器不会考虑对象的实际类型。
而不使用指针时比如以下这种情况:跟指针是不同的 !!:直接创建对象并调用函数。
class Base {
public:
void func() {
std::cout << "Base class func" << std::endl;
}
};
class Derived : public Base {
public:
void func() { // 这个函数屏蔽了基类中的func
std::cout << "Derived class func" << std::endl;
}
};
int main() {
Derived d;
d.func(); // 调用的将是Derived类中的func,输出: Derived class func
// Base类的func被屏蔽,无法通过Derived对象d直接调用
return 0;
}