调用SpriteSheet::Render触发读访问违规 this为nullptr报错
问题现象
在main.cpp中调用自定义函数时触发报错:Exception thrown: read access violation. this was nullptr。
- 仅执行
sprites->Render()调用时触发该错误,移除该调用则程序无异常 - 初步排查怀疑是指针或引用使用不当导致问题,但未定位到根因
- 附项目相关代码如下
项目相关代码
SpriteSheet.cpp
#include "SpriteSheet.h" #include <wincodec.h> #pragma comment(lib, "windowscodecs.lib") SpriteSheet::SpriteSheet(const wchar_t* filename, D2DGraphics* graphics) { // Init: this->graphics = graphics; // Save the graphics parameter for later bmp = NULL; //NULL our ID2D1Bitmap to start with HRESULT hr; // HRESULT for checking errors // Create a WIC factory IWICImagingFactory* wicFactory = NULL; hr = CoCreateInstance( CLSID_WICImagingFactory, // CLS ID of the object we're making NULL, // Not part of the aggregate CLSCTX_INPROC_SERVER, // DLL runs in the same process IID_IWICImagingFactory, // Ref to the interface that communicates with the object. (LPVOID*)&wicFactory); //The pointer that'll contain our factory. // Create a decoder IWICBitmapDecoder* wicDecoder = NULL; hr = wicFactory->CreateDecoderFromFilename( filename, // The file name NULL, // No preferred vendor GENERIC_READ, // We're reading the file, not writing WICDecodeMetadataCacheOnLoad, // Cache on load* &wicDecoder); // The decoder we're making // Read a frame from the image: IWICBitmapFrameDecode* wicFrame = NULL; hr = wicDecoder->GetFrame(0, &wicFrame); // Create a converter IWICFormatConverter* wicConverter = NULL; hr = wicFactory->CreateFormatConverter(&wicConverter); // Setup the converter hr = wicConverter->Initialize( wicFrame, //Frame GUID_WICPixelFormat32bppPBGRA, // Pixel Format WICBitmapDitherTypeNone, // No dithering NULL, // No pallete needed 0.0, // Alpha Transparency WICBitmapPaletteTypeCustom ); // Use the converter to create a D2D1Bitmap graphics->GetRenderTarget()->CreateBitmapFromWicBitmap( wicConverter, //The Converter NULL, // D2D1_BITMAP_PROPERTIES &bmp // Desitnation D2D1 bitmap ); if (wicFactory) wicFactory->Release(); if (wicDecoder) wicDecoder->Release(); if (wicConverter) wicConverter->Release(); if (wicFrame) wicFrame->Release(); } SpriteSheet::~SpriteSheet() { if (bmp) bmp->Release(); } // Draw the entire image at (0,0) void SpriteSheet::Draw() { graphics->GetRenderTarget()->DrawBitmap( bmp, // Bitmap D2D1::RectF(0.0f, 0.0f, bmp->GetSize().width, bmp->GetSize().height), //Destintation Rectangle 1.0f, //Opactity D2D1_BITMAP_INTERPOLATION_MODE::D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,// Interporlation is when image is stretched or shrunk. Nearest neighbor is the fastest method. D2D1::RectF(0.0f, 0.0f, bmp->GetSize().width, bmp->GetSize().height) // Source Rectangle ); } void SpriteSheet::Load() { //y = ySpeed = 0.0f; sprites = new SpriteSheet(L"circleL.png", graphics); } void SpriteSheet::Unload() { delete sprites; } void SpriteSheet::Render() { graphics->ClearScreen(0.0f, 0.0f, 0.5f); sprites->Draw(); }
SpriteSheet.h
#pragma once #include <wincodec.h> // WIC codec header for decoding image from file #include "D2DGraphics.h" // Includes windows and D2d1.h #include <d2d1.h> // Link to "windows codecs.lib", added this to the Linker->Input directory. class SpriteSheet { D2DGraphics* graphics; // Reference to the graphics class ID2D1Bitmap* bmp; // Bitmap loaded file and converted to Direct2D format float y; float ySpeed; SpriteSheet* sprites; public: // Constructor SpriteSheet(const wchar_t* filename, D2DGraphics* gfx); // Destructor ~SpriteSheet(); // Draw the bitmap to the render target void Draw(); void Load(); void Unload() ; void Render() ; };
main.cpp 相关代码
全局指针声明:
SpriteSheet* sprites; // create global spritesheet object.
WinMain函数中调用逻辑:
sprites->Render();
根因定位
- 直接触发报错的原因:全局声明的
SpriteSheet* sprites是原生指针,全局作用域下未手动初始化的原生指针默认值为nullptr,代码中从未通过new为该指针分配实际的SpriteSheet实例,就直接调用成员方法Render()。调用类成员方法时会隐式传入对象指针作为this参数,此时this就是空指针,访问类成员时直接触发读访问违规。 - 潜在的二级空指针问题:SpriteSheet类内部的成员变量
SpriteSheet* sprites没有在构造函数中初始化,属于野指针;即使修复了全局指针的初始化问题,如果没有提前调用Load()方法为该内部指针分配实例,执行到Render方法内的sprites->Draw()时,同样会触发空指针错误。 - 额外隐患:类中
y、ySpeed两个float成员同样未在构造函数中初始化,会生成随机脏值,后续使用可能触发逻辑异常。
修复方案
- 调用
sprites->Render()前必须先完成全局指针的实例化,同时调用Load()初始化内部精灵对象,注意需要先完成D2DGraphics对象的初始化再创建SpriteSheet实例:// 假设gfx是已经初始化完成的D2DGraphics实例 sprites = new SpriteSheet(L"你的主精灵资源路径.png", &gfx); sprites->Load(); // 初始化内部的sprites成员 // 之后再调用Render sprites->Render(); - 为SpriteSheet构造函数添加成员初始化列表,确保所有成员变量都有明确初始值,杜绝野指针和脏值:
SpriteSheet::SpriteSheet(const wchar_t* filename, D2DGraphics* graphics) : graphics(graphics), bmp(nullptr), y(0.0f), ySpeed(0.0f), sprites(nullptr) { // 保留原有构造函数内的WIC加载、位图创建逻辑即可 } - Render方法中增加空指针判断,避免内部sprites未加载时调用Draw:
void SpriteSheet::Render() { graphics->ClearScreen(0.0f, 0.0f, 0.5f); if (sprites != nullptr) { sprites->Draw(); } } - 程序退出前主动释放资源,避免内存泄漏:
// 消息循环结束、程序退出前执行 sprites->Unload(); delete sprites; sprites = nullptr;
内容的提问来源于stack exchange,提问作者Spencer349
相关产品推荐
相关产品推荐

