为什么C++父类指针可指向子类对象但不能调用子类成员函数
问题原理讲解
核心原因:C++的静态类型检查与非虚函数的静态绑定
- C++编译器在处理类成员函数调用时,对于非虚函数会执行静态绑定(编译期绑定),只会根据指针/引用的静态类型(也就是声明时的类型,这里是
Pet*)去对应类的定义中查找成员函数。 - 你的
Pet基类定义中,根本没有声明make_sound这个成员函数,因此编译器在编译a_pet1->make_sound()时,在Pet的成员列表中找不到对应函数,直接报编译错误,和指针实际指向的是子类对象无关。
补充说明:动态多态的实现要求
如果你想要通过基类指针调用子类特有的、不同实现的同名函数,需要用到C++的动态多态特性,满足两个条件:
- 基类中声明对应的虚函数(可以是纯虚函数,将基类设为抽象类)
- 子类重写该虚函数
此时编译器会执行动态绑定(运行期绑定),根据指针/引用实际指向的对象类型(动态类型)调用对应版本的函数。
修正后的可运行代码示例
#include <iostream> #include <string> using namespace std; class Pet { protected: string name; public: Pet(string n) { name = n; } void run() { cout << name << ": I'm running" << endl; } // 基类声明纯虚函数,Pet成为抽象类,不能实例化 virtual void make_sound() = 0; }; class Dog : public Pet { public: Dog(string n) : Pet(n) {}; // 子类重写虚函数,C++11及以上可以加override关键字显式标注 void make_sound() override { cout << name << ": Woof! Woof!" << endl; } }; class Cat : public Pet { public: Cat(string n) : Pet(n) {}; void make_sound() override { cout << name << ": Meow! Meow!" << endl; } }; int main() { Pet *a_pet1 = new Cat("Tom"); Pet *a_pet2 = new Dog("Spike"); a_pet1 -> run(); a_pet1 -> make_sound(); // 现在可以正常调用,会执行Cat类的make_sound a_pet2 -> run(); a_pet2 -> make_sound(); // 会执行Dog类的make_sound // 释放堆内存避免泄漏 delete a_pet1; delete a_pet2; return 0; }
替代方案:向下转型(不推荐)
如果不想修改基类定义,也可以通过dynamic_cast将基类指针安全转为子类指针后调用make_sound,但该方案需要额外判断转型是否成功,性能和可维护性都远低于虚函数实现的多态,仅适合特殊场景使用:
// main函数中调用示例 Pet *a_pet1 = new Cat("Tom"); if (Cat* cat_ptr = dynamic_cast<Cat*>(a_pet1)) { cat_ptr->make_sound(); // 转型成功才调用 }
内容的提问来源于stack exchange,提问作者taurus05
相关产品推荐
相关产品推荐

