C++中通过基类指针调用基类方法如何访问派生类新增的数据
崩溃原因
你猜测的原因不对,drawable是基类的protected成员,派生类向其中插入指针、基类draw方法读取的是同一个向量,这部分逻辑没有问题。崩溃的核心原因是你初始化player成员的写法触发了野指针:
你在Level构造函数中写的player = Player(500.f, 500.f)会先生成一个临时的Player对象,临时对象的构造函数会把自己内部shape的地址插入自身的drawable向量。之后调用默认赋值运算符把临时对象的内容浅拷贝给成员变量player,临时对象随即销毁,此时player的drawable向量里保存的是已经销毁的临时对象的shape地址,属于悬垂指针,调用draw方法访问这块内存就会触发崩溃。
解决方案
1. 改用成员初始化列表初始化成员
直接在Level构造函数的初始化列表中构造player,避免生成临时对象:
Level::Level() : player(500.f, 500.f) { gameObjects.push_back(&player); }
这种方式会直接调用成员player的构造函数,drawable中插入的就是成员player自身shape的地址,只要player的生命周期和Level一致,指针就不会失效。
2. 禁止基类及派生类的拷贝/移动操作
因为你的类中存储了指向自身成员的指针,一旦发生拷贝、移动操作,指针就会失效,建议直接在基类中删除相关操作,从编译层面避免误写:
class GameObject { public: GameObject() = default; // 禁止拷贝、移动 GameObject(const GameObject&) = delete; GameObject& operator=(const GameObject&) = delete; GameObject(GameObject&&) = delete; GameObject& operator=(GameObject&&) = delete; // 添加虚析构,保证基类指针销毁派生类对象时能正确调用派生类析构 virtual ~GameObject() = default; protected: std::vector<Drawable*> drawable; // ... };
加了上述声明后,你之前写的player = Player(...)会直接编译报错,避免运行时才发现问题。
3. 可选:如需支持拷贝可自定义虚更新接口
如果你的业务场景需要支持派生类的拷贝、移动,可以在基类中声明虚接口,派生类自行实现更新drawable向量的逻辑,拷贝后调用该接口刷新指针即可。
内容的提问来源于stack exchange,提问作者solacerxt
相关产品推荐
相关产品推荐

