为何此处virtual未调用重写函数?如何修正该问题?
问题分析与解决方案
问题根源
你代码里的核心问题是成员变量没有多态特性:
- B类中的
MemB* current只是隐藏了A类的MemA* current,但A类的getMemClass()成员函数只会访问自身类作用域内的current指针,也就是A构造函数中创建的MemA实例,所以输出的是MemA的内容。 - 虚函数的多态是针对成员函数的,成员变量不存在运行时动态绑定的机制,同名变量只会发生作用域隐藏。
可行解决方案
方案1:将getMemClass声明为虚函数,在B中重写
把A类的getMemClass改成虚函数,然后在B类中重写这个方法,让它访问B自己的current指针:
#include <iostream> struct MemA { virtual void tellClass() { std::cout << "I am member of class A" << std::endl; } }; struct MemB : public MemA { void tellClass() { std::cout << "I am member of class B" << std::endl; } }; class A { MemA *current; public: A() : current(new MemA()) {} // 声明为虚函数 virtual void getMemClass() { current->tellClass(); } virtual ~A() { delete current; } // 基类析构设为虚,避免内存泄漏 }; class B : public A { MemB *current; public: B() : A(), current(new MemB()) {} // 重写getMemClass,访问B自己的current void getMemClass() override { current->tellClass(); } ~B() { delete current; } }; int main() { B().getMemClass(); } // 标准C++中main返回类型必须是int
调用B对象的getMemClass()时,会触发虚函数动态绑定,执行B类的重写版本,输出I am member of class B。
方案2:复用A类的current指针,避免重复定义
既然MemB是MemA的子类,完全可以让A类的current指针指向MemB实例,不需要在B类中重新定义current:
#include <iostream> struct MemA { virtual void tellClass() { std::cout << "I am member of class A" << std::endl; } }; struct MemB : public MemA { void tellClass() { std::cout << "I am member of class B" << std::endl; } }; class A { protected: // 改为protected,让子类可以访问 MemA *current; public: A() : current(new MemA()) {} void getMemClass() { current->tellClass(); } virtual ~A() { delete current; } // 基类析构设为虚 }; class B : public A { public: B() : A() { // 先删除A构造的MemA实例,再替换为MemB delete current; current = new MemB(); } }; int main() { B().getMemClass(); }
这种方案利用MemA和MemB的多态性,让A类的current指向MemB对象,调用tellClass()时自动触发动态绑定,输出目标内容。
方案3:使用工厂方法模式(更优雅的设计)
如果后续还有更多MemA的子类,可以给A类添加一个虚的工厂方法,用来创建MemA实例,子类重写这个方法返回对应子类的实例:
#include <iostream> struct MemA { virtual void tellClass() { std::cout << "I am member of class A" << std::endl; } }; struct MemB : public MemA { void tellClass() { std::cout << "I am member of class B" << std::endl; } }; class A { MemA *current; // 虚工厂方法,子类重写 virtual MemA* createMem() { return new MemA(); } public: A() : current(createMem()) {} void getMemClass() { current->tellClass(); } virtual ~A() { delete current; } }; class B : public A { MemB* createMem() override { return new MemB(); } }; int main() { B().getMemClass(); }
这种设计符合开闭原则,新增子类时不需要修改A类的逻辑,只需要重写createMem()方法即可。
额外注意点
- 标准C++中
main函数的返回类型必须是int,不能是void,这是编译器的标准要求。 - 基类的析构函数必须声明为虚函数,否则当通过基类指针删除子类对象时,会导致子类的析构函数不被调用,引发内存泄漏。
内容的提问来源于stack exchange,提问作者Chandramouli Mukherjee
相关产品推荐
相关产品推荐

