2017-05-08 113 views
-2

我試圖呈現多個SDL_Rect s,每個都有自己的SDL_Texture和單行文本,以形成多行文本框。當其運行CTextInput正常工作,然後當我按返回文本呈現到第一行,然後突然崩潰,報告內存泄漏。向量返回內存異常錯誤

我正在使用類來解決這個問題。每個CTextBox對象都有自己的文本行,將其渲染爲SDL_Surface,然後渲染爲SDL_TextureSDL_Texture,即mTexture。類CTextMenu被製成行政類。所有CTextBox對象獲取存儲在boxes,這是std::vector<CTextBox> boxes,這是由CMenu(的CTextMenu)主類對象。當CMenu.update()被調用時,boxes所有元素在for循環被渲染到SDL_Rect rcTextOutput擁有。

這裏是上述for環和CTextMenu類:

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我會後整個腳本,頭和來源,下面。

我也跑了並輸入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; 
} 

回答

2

的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() ...使用正確初始化的,仍然有效的迭代器,並且問題應該消失。

+0

我在類的構造函數中加入了'it = boxes.begin()',就像你說的那樣,但它有相同的錯誤。你能給我一個「正確的構造函數」的例子嗎? –

+0

沒關係。我除去'的std ::矢量<> :: iterator'共和剛使用'boxes.insert(boxes.begin(),箱)'。 –

+0

對不起,關於構造函數的混亂,這暗示是誤導。您應該始終確保所有類的成員通過適當的構造函數初始化,迭代器通常應該永遠是擺在首位的對象的一部分:當他們遍歷容器重新分配內存迭代器將成爲不確定。因此,在任何延長的時間內保持迭代器是危險的。使用'CTextMenu :: newBox()內的迭代器'就可以了,在'CTextMenu'很可能不會堅持它。我應該有這個思想輸入我的答案... – cmaster