You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用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成员同样未在构造函数中初始化,会生成随机脏值,后续使用可能触发逻辑异常。
修复方案
  1. 调用sprites->Render()前必须先完成全局指针的实例化,同时调用Load()初始化内部精灵对象,注意需要先完成D2DGraphics对象的初始化再创建SpriteSheet实例:
    // 假设gfx是已经初始化完成的D2DGraphics实例
    sprites = new SpriteSheet(L"你的主精灵资源路径.png", &gfx);
    sprites->Load(); // 初始化内部的sprites成员
    // 之后再调用Render
    sprites->Render();
    
  2. 为SpriteSheet构造函数添加成员初始化列表,确保所有成员变量都有明确初始值,杜绝野指针和脏值:
    SpriteSheet::SpriteSheet(const wchar_t* filename, D2DGraphics* graphics)
        : graphics(graphics), bmp(nullptr), y(0.0f), ySpeed(0.0f), sprites(nullptr)
    {
        // 保留原有构造函数内的WIC加载、位图创建逻辑即可
    }
    
  3. Render方法中增加空指针判断,避免内部sprites未加载时调用Draw:
    void SpriteSheet::Render()
    {
        graphics->ClearScreen(0.0f, 0.0f, 0.5f);
        if (sprites != nullptr)
        {
            sprites->Draw();
        }
    }
    
  4. 程序退出前主动释放资源,避免内存泄漏:
    // 消息循环结束、程序退出前执行
    sprites->Unload();
    delete sprites;
    sprites = nullptr;
    

内容的提问来源于stack exchange,提问作者Spencer349

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 02:03:49