向量返回内存异常错误

Vector returning a memory exception error

我正在尝试渲染多个 SDL_Rect,每个都有自己的 SDL_Texture 和 single-lined 文本,以形成一个 multi-lined 文本框。当它运行时 CTextInput 正常工作,然后当我按 RETURN 时文本呈现到第一行然后突然崩溃,报告内存泄漏。

我正在使用 classes 来解决这个问题。每个 CTextBox object 都有自己的一行文本,被渲染成 SDL_Surface,然后渲染成 SDL_Texture,而 SDL_Texture 被渲染成 mTexture. class CTextMenu 被设为管理 class。所有 CTextBox object 都存储在 boxes 中,这是 std::vector<CTextBox> boxes,属于 CMenu(主 class object of CTextMenu)。当调用 CMenu.update() 时,boxes 中的所有元素都在 for 循环中渲染到 SDL_Rect rcTextOutput

这是前面提到的 for 循环和 CTextMenu class:

class CTextMenu
{
  std::vector<CTextBox> boxes;
  std::vector<CTextBox>::iterator it;
public:
  void update(SDL_Renderer *renderer, TTF_Font *font, SDL_Color color, SDL_Rect output);
  void newBox(std::string text);
};

void CTextMenu::update(SDL_Renderer *renderer, TTF_Font *font, SDL_Color color, SDL_Rect output)
{
  SDL_RenderSetClipRect(renderer, &output);
  for (unsigned int i = 0; i < boxes.(); i++)
  {
    boxes[i].render(renderer, font, boxes[i].text, color);
    SDL_Rect dstrect;
    dstrect.x = 0;
    dstrect.y = 600 + i * boxes[i].getHeight();
    dstrect.w = boxes[i].getWidth();
    dstrect.h = boxes[i].getHeight();
    SDL_RenderCopy(renderer, boxes[i].getTexture(), NULL, &dstrect);
  }
}

void CTextMenu::newBox(std::string text)
{
  CTextBox box;
  box.text = text;
  boxes.insert(it, box);
  if (boxes.size() > 14)
  {
    boxes.pop_back();
  }
}

在我看来,我的处理方式有些不对劲 mTexture。我将 post 整个脚本,header 和源代码,如下。

我也 运行 并输入了 textRETURN。它返回:

==3485== Memcheck, a memory error detector
==3485== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3485== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3485== Command: build/./caventure
==3485== 
==3485== Syscall param writev(vector[...]) points to uninitialised byte(s)
==3485==    at 0x5CF218D: ??? (in /usr/lib/libc-2.25.so)
==3485==    by 0x8088BAC: ??? (in /usr/lib/libxcb.so.1.1.0)
==3485==    by 0x8088FAC: ??? (in /usr/lib/libxcb.so.1.1.0)
==3485==    by 0x808902C: xcb_writev (in /usr/lib/libxcb.so.1.1.0)
==3485==    by 0x7D7EF3D: _XSend (in /usr/lib/libX11.so.6.3.0)
==3485==    by 0x7D7F431: _XReply (in /usr/lib/libX11.so.6.3.0)
==3485==    by 0x7D6A2EE: XInternAtom (in /usr/lib/libX11.so.6.3.0)
==3485==    by 0x4EFB79A: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==    by 0x4EFC694: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==    by 0x4EEB87F: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==    by 0x4EEB60E: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==    by 0x4E4F1C6: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==  Address 0x79c5573 is 35 bytes inside a block of size 16,384 alloc'd
==3485==    at 0x4C2CF35: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3485==    by 0x7D6F385: XOpenDisplay (in /usr/lib/libX11.so.6.3.0)
==3485==    by 0x4EFA84F: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==    by 0x4EEB5BB: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==    by 0x4E4F1C6: ??? (in /usr/lib/libSDL2-2.0.so.0.4.1)
==3485==    by 0x402002: init() (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x402333: main (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485== 
==3485== Invalid write of size 4
==3485==    at 0x402BEC: CTextBox::CTextBox(CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x403B6B: void __gnu_cxx::new_allocator<CTextBox>::construct<CTextBox, CTextBox const&>(CTextBox*, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4030EE: void std::allocator_traits<std::allocator<CTextBox> >::construct<CTextBox, CTextBox const&>(std::allocator<CTextBox>&, CTextBox*, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4037FB: void std::vector<CTextBox, std::allocator<CTextBox> >::_M_insert_aux<CTextBox const&>(__gnu_cxx::__normal_iterator<CTextBox*, std::vector<CTextBox, std::allocator<CTextBox> > >, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x402E16: std::vector<CTextBox, std::allocator<CTextBox> >::insert(__gnu_cxx::__normal_iterator<CTextBox const*, std::vector<CTextBox, std::allocator<CTextBox> > >, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x401F9F: CTextMenu::newBox(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4027A6: main (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==  Address 0xfffffffff6221c30 is not stack'd, malloc'd or (recently) free'd
==3485== 
==3485== 
==3485== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==3485==  Access not within mapped region at address 0xFFFFFFFFF6221C30
==3485==    at 0x402BEC: CTextBox::CTextBox(CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x403B6B: void __gnu_cxx::new_allocator<CTextBox>::construct<CTextBox, CTextBox const&>(CTextBox*, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4030EE: void std::allocator_traits<std::allocator<CTextBox> >::construct<CTextBox, CTextBox const&>(std::allocator<CTextBox>&, CTextBox*, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4037FB: void std::vector<CTextBox, std::allocator<CTextBox> >::_M_insert_aux<CTextBox const&>(__gnu_cxx::__normal_iterator<CTextBox*, std::vector<CTextBox, std::allocator<CTextBox> > >, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x402E16: std::vector<CTextBox, std::allocator<CTextBox> >::insert(__gnu_cxx::__normal_iterator<CTextBox const*, std::vector<CTextBox, std::allocator<CTextBox> > >, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x401F9F: CTextMenu::newBox(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4027A6: main (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==  If you believe this happened as a result of a stack
==3485==  overflow in your program's main thread (unlikely but
==3485==  possible), you can try to increase the size of the
==3485==  main thread stack using the --main-stacksize= flag.
==3485==  The main thread stack size used in this run was 8388608.
==3485== 
==3485== HEAP SUMMARY:
==3485==     in use at exit: 14,025,073 bytes in 36,583 blocks
==3485==   total heap usage: 210,985 allocs, 174,402 frees, 97,089,179 bytes allocated
==3485== 
==3485== LEAK SUMMARY:
==3485==    definitely lost: 16 bytes in 1 blocks
==3485==    indirectly lost: 176 bytes in 4 blocks
==3485==      possibly lost: 4,262,172 bytes in 30,166 blocks
==3485==    still reachable: 9,762,709 bytes in 6,412 blocks
==3485==         suppressed: 0 bytes in 0 blocks
==3485== Rerun with --leak-check=full to see details of leaked memory
==3485== 
==3485== For counts of detected and suppressed errors, rerun with: -v
==3485== Use --track-origins=yes to see where uninitialised values come from
==3485== ERROR SUMMARY: 6 errors from 2 contexts (suppressed: 0 from 0)

此外,如果 中有更有效的方式来呈现这样的文本框,我将很高兴听到。


gfx.h:

#ifndef __GFX_H__
#define __GFX_H__

#include <vector>
#include <string>
#include <stdexcept>
#include <algorithm>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

class CTextBox
{
public:
  CTextBox();
  ~CTextBox();
  void free();
  void render(SDL_Renderer* renderer, TTF_Font* font, std::string text, SDL_Color color);
  int getWidth();
  int getHeight();
  SDL_Texture* getTexture();
  std::string text;
private:
  SDL_Texture* mTexture;

  int mWidth;
  int mHeight;
};

CTextBox::CTextBox()
{
  mTexture = NULL;
  mWidth = 800;
  mHeight = 20;
  text = " ";
}

void CTextBox::free()
{
  if (mTexture != NULL)
  {
    SDL_DestroyTexture(mTexture);
    mTexture = NULL;
  }
}

CTextBox::~CTextBox()
{
  free();
}

void CTextBox::render(SDL_Renderer* renderer, TTF_Font* font, std::string text, SDL_Color color)
{
  free();

  SDL_Surface* TextSurface = TTF_RenderText_Solid(font, text.c_str(), color);
  if (TextSurface == NULL)
  {
    throw(::std::runtime_error("Unable to render surface! ERROR: "));
  }
  mTexture = SDL_CreateTextureFromSurface(renderer, TextSurface);
  if (mTexture == NULL)
  {
    throw(::std::runtime_error("Unable to render texture! ERROR: "));
  }
  SDL_FreeSurface(TextSurface);

  SDL_QueryTexture(mTexture, NULL, NULL, &mWidth, &mHeight);
}

int CTextBox::getWidth()
{
  return mWidth;
}

int CTextBox::getHeight()
{
  return mHeight;
}

SDL_Texture* CTextBox::getTexture()
{
  return mTexture;
}

class CTextMenu
{
  std::vector<CTextBox> boxes;
  std::vector<CTextBox>::iterator it;
public:
  void update(SDL_Renderer *renderer, TTF_Font *font, SDL_Color color, SDL_Rect output);
  void newBox(std::string text);
};

void CTextMenu::update(SDL_Renderer *renderer, TTF_Font *font, SDL_Color color, SDL_Rect output)
{
  SDL_RenderSetClipRect(renderer, &output);
  for (unsigned int i = 0; i < boxes.(); i++)
  {
    boxes[i].render(renderer, font, boxes[i].text, color);
    SDL_Rect dstrect;
    dstrect.x = 0;
    dstrect.y = 600 + i * boxes[i].getHeight();
    dstrect.w = boxes[i].getWidth();
    dstrect.h = boxes[i].getHeight();
    SDL_RenderCopy(renderer, boxes[i].getTexture(), NULL, &dstrect);
  }
}

void CTextMenu::newBox(std::string text)
{
  CTextBox box;
  box.text = text;
  boxes.insert(it, box);
  if (boxes.size() > 14)
  {
    boxes.pop_back();
  }
}

#endif

gfx.cpp:

#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#include "gfx.h"

// Screen dimensions, constants
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 900; // 600 for ground, 280 for output, 20 for input

SDL_Window* gWindow = NULL; // The window we'll be rendering to
SDL_Surface* gScreenSurface = NULL; // The surface contained by the window
SDL_Surface* gCurrentSurface = NULL; // Current displayed image
TTF_Font* gFont = NULL; // Font pointer.
SDL_Color gTextColor = { 255, 255, 255, 0xFF }; // Text color, white.

CTextMenu CMenu;
CTextBox CTextInput;

SDL_Renderer* gRenderer = NULL; // The renderer we'll be using
SDL_Rect rcGround, rcSprite, rcTextInput, rcTextOutput, rcTextOutputGrd;

void init();
void loadMedia();
void quit();

void init()
{
  if (SDL_Init(SDL_INIT_VIDEO) > 0)
  {
    throw(::std::runtime_error("SDL failed to initialise! ERROR: "));
  }
  else
  {
    gWindow = SDL_CreateWindow("Caventure",
                                SDL_WINDOWPOS_UNDEFINED,
                                SDL_WINDOWPOS_UNDEFINED,
                                SCREEN_WIDTH,
                                SCREEN_HEIGHT,
                                SDL_WINDOW_SHOWN);
    if (gWindow == NULL)
    {
      throw(::std::runtime_error("Window failed to initialise! ERROR: "));
    }
    else
    {
      gScreenSurface = SDL_GetWindowSurface(gWindow);
    }
    gRenderer = SDL_CreateRenderer(gWindow,
                                    -1,
                                    0);
    if (gRenderer == NULL)
    {
      throw(::std::runtime_error("Renderer could not be initialised! ERROR: "));
    }
    if (TTF_Init() > 0)
    {
      throw(::std::runtime_error("TTF could not be initialised! ERROR: "));
    }
  }
}

void loadMedia()
{
  // Ground rendering
  rcGround.x = 0;
  rcGround.y = 0;
  rcGround.w = 800;
  rcGround.h = 600;

  // Sprite rendering
  rcSprite.x = 400;
  rcSprite.y = 300;
  rcSprite.w = 4;
  rcSprite.h = 4;

  // TextOutput box rendering
  rcTextOutput.x = 0;
  rcTextOutput.y = 600;
  rcTextOutput.w = 800;
  rcTextOutput.h = 280;

  // TextOutput box rendering
  rcTextOutputGrd.x = 0;
  rcTextOutputGrd.y = 600;
  rcTextOutputGrd.w = 800;
  rcTextOutputGrd.h = 280;

  gFont = TTF_OpenFont("src/graphics/resources/notomono-regular.ttf", 14);
  if (gFont == NULL)
  {
    throw(::std::runtime_error("Font load error"));
  }

  SDL_SetTextInputRect(&rcTextInput);
}

void quit()
{
  // Destroy window
    SDL_DestroyWindow(gWindow);
    SDL_DestroyRenderer(gRenderer);
  TTF_CloseFont(gFont);
    gWindow = NULL;
  gRenderer = NULL;
  gFont = NULL;

    // Quit SDL subsystems
  TTF_Quit();
    SDL_Quit();
}

int main()
{
  try
  {
    init();
    loadMedia();

    bool quit = false;
    bool renderText = false;
    SDL_Event event;
    std::string inputText = " ";
    std::string inputCmd = "";
    SDL_StartTextInput();

    while(!quit)
    {
      while(SDL_PollEvent(&event) != 0)
      {
        if(event.type == SDL_QUIT)
        {
          quit = true;
        }
        else if(event.type == SDL_KEYDOWN)
        {
          switch(event.key.keysym.sym)
          {
            case SDLK_UP:
            rcSprite.y -= 5;
            break;

            case SDLK_DOWN:
            rcSprite.y += 5;
            break;

            case SDLK_LEFT:
            rcSprite.x -= 5;
            break;

            case SDLK_RIGHT:
            rcSprite.x += 5;
            break;
          }
          if (event.key.keysym.sym == SDLK_BACKSPACE && inputText.length() > 0)
          {
            inputText.pop_back();
            if (inputText.length() == 0)
            {
              inputText = " ";
            }
          }
          else if (event.key.keysym.sym == SDLK_RETURN && inputText.length() != 0)
          {
            inputCmd = inputText.c_str();
            renderText = true;
            inputText = " ";
          }
        }
        else if (event.type == SDL_TEXTINPUT)
        {
          inputText += event.text.text;
        }
      }
      if (rcSprite.x < 0 || rcSprite.y < 0 || rcSprite.y > rcGround.h || rcSprite.x > rcGround.w)
      {
        rcSprite.x = 400;
        rcSprite.y = 300;
      }

      SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0x00);
      SDL_RenderClear(gRenderer);

      SDL_RenderFillRect(gRenderer, &rcGround);
      SDL_BlitSurface(gCurrentSurface, NULL, gScreenSurface, &rcGround);

      SDL_SetRenderDrawColor(gRenderer, 0x40, 0x40, 0x40, 0x40);
      SDL_RenderFillRect(gRenderer, &rcTextOutputGrd);
      SDL_BlitSurface(gCurrentSurface, NULL, gScreenSurface, &rcTextOutputGrd);

      // Text input
      CTextInput.render(gRenderer, gFont, inputText.c_str(), gTextColor);
      rcTextInput.x = 0;
      rcTextInput.y = 880;
      rcTextInput.w = CTextInput.getWidth();
      rcTextInput.h = CTextInput.getHeight();
      SDL_RenderCopy(gRenderer, CTextInput.getTexture(), NULL, &rcTextInput);

      if (renderText)
      {
        // Text output
        CMenu.newBox(inputCmd.c_str());
        CMenu.update(gRenderer, gFont, gTextColor, rcTextOutput);
      }
      SDL_RenderSetClipRect(gRenderer, NULL);
      SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);

      SDL_RenderDrawLine(gRenderer, 0, 600, 800, 600);
      SDL_RenderDrawLine(gRenderer, 0, 880, 800, 880);

      SDL_RenderFillRect(gRenderer, &rcSprite);
      SDL_BlitSurface(gCurrentSurface, NULL, gScreenSurface, &rcSprite);

      SDL_RenderPresent(gRenderer);
    }
    SDL_StopTextInput();
  }
  catch (std::runtime_error const& msg)
    {
        printf("%s", msg.what());
        if (SDL_GetError() != NULL)
        {
            printf("%s", SDL_GetError());
        }
    else if (TTF_GetError() != NULL)
        {
            printf("%s", TTF_GetError());
        }
    else
    {
      printf("%s", "NULL");
    }
    quit();
        exit(EXIT_FAILURE);
    }
  quit();
  return 0;
}

valgrind 错误

==3485== Invalid write of size 4
==3485==    at 0x402BEC: CTextBox::CTextBox(CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x403B6B: void __gnu_cxx::new_allocator<CTextBox>::construct<CTextBox, CTextBox const&>(CTextBox*, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4030EE: void std::allocator_traits<std::allocator<CTextBox> >::construct<CTextBox, CTextBox const&>(std::allocator<CTextBox>&, CTextBox*, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4037FB: void std::vector<CTextBox, std::allocator<CTextBox> >::_M_insert_aux<CTextBox const&>(__gnu_cxx::__normal_iterator<CTextBox*, std::vector<CTextBox, std::allocator<CTextBox> > >, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x402E16: std::vector<CTextBox, std::allocator<CTextBox> >::insert(__gnu_cxx::__normal_iterator<CTextBox const*, std::vector<CTextBox, std::allocator<CTextBox> > >, CTextBox const&) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x401F9F: CTextMenu::newBox(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==    by 0x4027A6: main (in /home/pradana/Projects/sierra/caventure/build/caventure)
==3485==  Address 0xfffffffff6221c30 is not stack'd, malloc'd or (recently) free'd

告诉您您需要知道的一切:您正在使用未初始化的迭代器调用 std::vector<>::insert()...使用正确初始化且仍然有效的迭代器,问题应该消失。