std::vector导致指针无效

Pointer Invalidation with std::vector

本文关键字:指针 无效 vector std      更新时间:2023-10-16

我正在编写玩具C++代码来学习它和游戏开发,一路上包装SDL;它现在所做的就是加载一个BMP图像并在屏幕上循环显示。仅过帐相关代码。

编辑:我将把这篇文章修改到MVCE。这个问题可以更直接地找到。

#include <SDL.h>
#include <vector>
class Surface {
public:
    Surface(SDL_Surface*);
    ~Surface();
    SDL_Surface* mSurface;
}
Surface::Surface(SDL_Surface* surface) {
    mSurface = surface;
}
Surface::~Surface() {
    SDL_FreeSurface(mSurface);
}
int main(int argc, char* args[])
{
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        throw("Failed to SDL_Init: " + std::string(SDL_GetError()));
    }
    SDL_Window* window = SDL_CreateWindow("SDL TEST", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 960, SDL_WINDOW_SHOWN);
    SDL_Surface* screenSurface = SDL_GetWindowSurface(window);
    const char * imgPath = "test.bmp";
    std::vector<Surface> surfaces;
    surfaces.push_back(Surface(SDL_LoadBMP(imgPath)));
    SDL_BlitSurface(surfaces.begin()->mSurface, // memory access violation here
                            NULL, screenSurface, NULL);
    SDL_UpdateWindowSurface(window);
    SDL_Delay(2000);
    SDL_Quit();
    return 0;
}

归根结底,加载到vector中的Surface对象内部的指针变为无效。我四处阅读,认为这可能与需要一个自定义副本构造函数有关,但制作这样的构造函数并不能解决问题:

Surface::Surface(const Surface& other) {
    mSurface = other.mSurface;
    mIsWindowSurface = other.mIsWindowSurface;
}

您没有定义复制构造函数,所以使用了默认的复制构造函数,但这并不能满足您的要求。

在线

 surfaces.push_back(Surface(SDL_LoadBMP(imgPath)));

情况如下:

  1. 创建一个临时Surface,其mSurface指向新分配的曲面
  2. 使用复制构造函数(或复制赋值运算符)将此Surface复制到向量中
  3. 破坏调用SDL_FreeSurface的临时Surface

然后,当您继续执行surfaces.begin()->mSurface,时,您正在尝试使用已经释放的曲面。

要在C++11之前解决此问题,您需要使复制构造函数和复制赋值运算符在语义上进行复制,或者使用引用计数机制来确保只有在没有更多指向曲面的活动指针时才调用SDL_FreeSurface

在C++11中,有更多开箱即用的修复程序;其中之一是禁用类的复制,并实现移动构造函数和移动赋值运算符,和/或使用emplace_back( SDL_LoadBMP。。。而不是CCD_ 11。