我試圖呈現多個SDL_Rect
s,每個都有自己的SDL_Texture
和單行文本,以形成多行文本框。當其運行CTextInput
正常工作,然後當我按返回文本呈現到第一行,然後突然崩潰,報告內存泄漏。向量返回內存異常錯誤
我正在使用類來解決這個問題。每個CTextBox
對象都有自己的文本行,將其渲染爲SDL_Surface
,然後渲染爲SDL_Texture
和SDL_Texture
,即mTexture
。類CTextMenu
被製成行政類。所有CTextBox
對象獲取存儲在boxes
vector,這是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
我會後整個腳本,頭和來源,下面。
我也跑了valgrind並輸入text
RETURN。它返回:
==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)
而且,如果在sdl-2渲染這樣一個文本框的更有效的方式,我會很高興聽到這個消息。
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;
}
我在類的構造函數中加入了'it = boxes.begin()',就像你說的那樣,但它有相同的錯誤。你能給我一個「正確的構造函數」的例子嗎? –
沒關係。我除去'的std ::矢量<> :: iterator'共和剛使用'boxes.insert(boxes.begin(),箱)'。 –
對不起,關於構造函數的混亂,這暗示是誤導。您應該始終確保所有類的成員通過適當的構造函數初始化,迭代器通常應該永遠是擺在首位的對象的一部分:當他們遍歷容器重新分配內存迭代器將成爲不確定。因此,在任何延長的時間內保持迭代器是危險的。使用'CTextMenu :: newBox()內的迭代器'就可以了,在'CTextMenu'很可能不會堅持它。我應該有這個思想輸入我的答案... – cmaster