调用重写Draw函数时触发Access Violation崩溃求助
问题排查与解决
崩溃原因分析
你遇到的0xC0000005访问违规(读取地址0x00000000),本质是访问了悬空指针或空指针。结合代码来看,大概率是以下两种情况:
- 添加了临时对象的引用:比如调用
AddDot(Dot(10,20)),临时对象在语句执行结束后就会被销毁,容器里的指针变成指向已释放内存的悬空指针,后续调用Draw()时触发崩溃。 - 被添加的
Dot对象提前销毁:比如在局部作用域创建Dot对象,作用域结束后对象被销毁,容器内的指针同样变成悬空指针。
代码修复方案
核心解决思路是保证容器内指针指向的对象生命周期不短于容器的生命周期,以下是两种可靠方案:
方案1:用智能指针自动管理对象生命周期
改用std::vector<std::unique_ptr<Element>>,让智能指针自动处理内存释放,彻底避免悬空指针问题:
#include <memory> // 需包含智能指针头文件 class Drawing { public: std::vector<std::unique_ptr<Element>> Elements; // 直接传入坐标,在容器内构造Dot对象 void AddDot(int x, int y) { Elements.emplace_back(std::make_unique<Dot>(x, y)); } void Draw() { for (auto& element : Elements) { // 使用引用避免智能指针拷贝 element->Draw(); } } };
调用时直接传入坐标即可:drawing.AddDot(10, 20);,无需手动管理Dot对象的生命周期。
方案2:确保被添加对象的生命周期足够长
如果不想用智能指针,必须保证被添加的Dot对象在Drawing容器的整个生命周期内都存在:
// 示例1:使用全局对象,生命周期贯穿整个程序 Dot globalDot(10, 20); void DemoFunc() { Drawing drawing; drawing.AddDot(globalDot); // 添加全局对象的引用,指针不会悬空 } // 示例2:使用堆分配对象,手动控制生命周期 void DemoFunc2() { Drawing drawing; Dot* heapDot = new Dot(30, 40); drawing.Elements.emplace_back(heapDot); // 注意:程序结束或不再需要时,需手动释放堆内存避免泄漏 // for (auto elem : drawing.Elements) delete elem; }
额外优化建议
- 给基类
Element添加虚析构函数:因为使用了多态,基类没有虚析构函数的话,通过基类指针删除子类对象时不会调用子类析构函数,会导致内存泄漏:
class Element { public: ElementTypes type = DOT; Element() {} Element(ElementTypes type) : type(type) {} virtual void Draw() { return; } virtual ~Element() = default; // 添加虚析构函数 };
- 将
Elements容器设为私有,只通过成员函数对外提供操作接口,防止外部误操作导致指针异常。
内容的提问来源于stack exchange,提问作者stephan
相关产品推荐
相关产品推荐

