TTF_RenderText_Solid(...) 导致分段错误
TTF_RenderText_Solid(...) causes segmentation fault error
我正在尝试利用 class Text
来处理基于 c++ SDL2 的程序中的文本。
TTF_RenderText_Solid 在主函数中完美运行,尽管在我的 classe Text
中,在此处的这一行 SDL_Surface *surface = TTF_RenderText_Solid( font, text.c_str(), fg );
中,它导致了一些错误。有时它给我一个分段错误,有时却没有。
我调试了代码,所有三个变量 TTF_Font *font
、std::string text
和 SDL_Color fg
都具有各自正确的值。
我的主要功能:
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "Text.h"
int main()
{
try
{
SDL_Init( SDL_INIT_VIDEO );
TTF_Init();
SDL_Window *window = SDL_CreateWindow( "Window", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, 800, 800, SDL_WINDOW_SHOWN );
SDL_Renderer *renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );
TTF_Font *f = TTF_OpenFont( "cruft.ttf", 32 );
SDL_Surface *s = TTF_RenderText_Solid( f, "Avocado", {0,0,0,255} );
if(s == NULL)
std::cout << "s == NULL\n";
Text title;
title = Text( renderer, "cruft.ttf", 32, "title" );
title.render_Text_Solid( "Avocado", {0,0,0,255} );
SDL_Quit();
TTF_Quit();
return 0;
}
catch( std::exception& e )
{
std::cerr << "Error: " << e.what() << "\n";
return 0;
}
catch(...)
{
std::cerr << "Unkown error!\n";
return 0;
}
}
我的 Text.cpp
文件:
#include <iostream>
#include <string.h>
#include "Text.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
//Constructors
Text::Text(){}
Text::Text(SDL_Renderer *renderer, std::string file, int ptsize, std::string name)
{
set_renderer( renderer );
this->name = name;
set_TTF_Font( file, ptsize );
}
Text::~Text()
{
TTF_CloseFont( font );
SDL_DestroyTexture( texture );
}
void Text::set_renderer( SDL_Renderer *renderer )
{
if( renderer == NULL )
throw std::runtime_error( name + ": Renderer could not be set! renderer == NULL\n" + SDL_GetError() );
this->renderer = renderer;
}
void Text::set_TTF_Font( std::string file, int ptsize )
{
TTF_CloseFont( font );
font = NULL;
SDL_DestroyTexture( texture );
texture = NULL;
width = 0;
height = 0;
if( file == "" )
throw std::runtime_error( name + ": TTF_Font* could not be set! file == ""\n" + SDL_GetError() );
if( ptsize <= 0 )
throw std::runtime_error( name + ": TTF_Font* could not be set! ptsize <= 0\n" + SDL_GetError() );
TTF_Font *font = TTF_OpenFont( file.c_str(), ptsize );
if( font == NULL )
throw std::runtime_error( name + ": TTF_Font* could not be set! font == NULL\n" + SDL_GetError() );
this->font = font;
}
void Text::render_Text_Solid( std::string text, SDL_Color fg )
{
SDL_DestroyTexture( texture );
texture = NULL;
width = 0;
height = 0;
SDL_Surface *surface = TTF_RenderText_Solid( font, text.c_str(), fg );
if( surface == NULL )
throw std::runtime_error( name + ": Text could not be created! surface == NULL\n" + SDL_GetError() );
texture = SDL_CreateTextureFromSurface( renderer, surface );
width = surface->w;
height = surface->h;
SDL_FreeSurface( surface );
if( texture == NULL )
throw std::runtime_error( name + ": Text could not be created! texture == NULL\n" + SDL_GetError() );
}
我的 Text.h
文件:
#ifndef TEXT_H_INCLUDED
#define TEXT_H_INCLUDED
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
class Text
{
public:
//constructors
Text();
Text(SDL_Renderer *renderer, std::string file, int ptsize, std::string name);
~Text();
void set_renderer( SDL_Renderer *renderer );
void set_TTF_Font( std::string file, int ptsize );
void render_Text_Solid( std::string text, SDL_Color fg );
//render
void render( int x, int y );
void render( SDL_Rect *srcrect, SDL_Rect *dstrect );
//variables
int width = 0;
int height = 0;
private:
SDL_Renderer *renderer = NULL;
SDL_Texture *texture = NULL;
TTF_Font *font = NULL;
std::string name = "";
};
#endif // TEXT_H_INCLUDED
PS:我使用 Manjaro Linux 和 Codeblocks
我们先看看这几行是干什么的:
Text title;
title = Text( renderer, "cruft.ttf", 32, "title" );
首先创建 title
,并使用默认构造函数 Text::Text()
对其进行初始化,这会为其字段设置默认值。然后,您使用专门的构造函数创建第二个 Text
(为清楚起见,我们将其称为 title1
)对象。然后将 title1
复制到 title
- 由于未定义 operator=
,因此生成默认副本。现在您的 title
与 title1
具有相同的值。 title1.~Text()
被调用,杀死你的 font
.
你所拥有的是你的 title.font
仍然有它以前的值,但它指向已经关闭的字体结构。
这可以通过不构建临时 Text
对象来避免 - 例如Text title = Text(.......)
不会产生临时对象和复制。这只是一种解决方法,实际问题仍然存在 - 您的 Text
对象无法安全复制。有很多方法可以解决这个问题——比如使用某种 unique_ptr
包装器或放弃析构函数以支持手动 deinit 方法等。
这给我们带来了下一个问题 - 如果您解决了复制问题,您现在可以生成文本表面和纹理,但请查看您的退出代码:
Text title;
// ... omitted
SDL_Quit();
TTF_Quit();
return 0;
您的最终确定是相反的 - 您想要关闭字体、删除纹理、销毁 renderer/window、然后 调用 TTF_Quit
和 SDL_Quit
,而是你有 SDL_Quit()
、TTF_Quit()
,并且只有在那之后 title.~Text()
- 可能会崩溃,因为 SDL/TTF 已经完成并且你不应该执行 SDL在那之后打电话。当然这也可以解决,例如通过将您的 title
包含在额外的代码块中,但是要注意的东西的数量会变得太大。
我正在尝试利用 class Text
来处理基于 c++ SDL2 的程序中的文本。
TTF_RenderText_Solid 在主函数中完美运行,尽管在我的 classe Text
中,在此处的这一行 SDL_Surface *surface = TTF_RenderText_Solid( font, text.c_str(), fg );
中,它导致了一些错误。有时它给我一个分段错误,有时却没有。
我调试了代码,所有三个变量 TTF_Font *font
、std::string text
和 SDL_Color fg
都具有各自正确的值。
我的主要功能:
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "Text.h"
int main()
{
try
{
SDL_Init( SDL_INIT_VIDEO );
TTF_Init();
SDL_Window *window = SDL_CreateWindow( "Window", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, 800, 800, SDL_WINDOW_SHOWN );
SDL_Renderer *renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );
TTF_Font *f = TTF_OpenFont( "cruft.ttf", 32 );
SDL_Surface *s = TTF_RenderText_Solid( f, "Avocado", {0,0,0,255} );
if(s == NULL)
std::cout << "s == NULL\n";
Text title;
title = Text( renderer, "cruft.ttf", 32, "title" );
title.render_Text_Solid( "Avocado", {0,0,0,255} );
SDL_Quit();
TTF_Quit();
return 0;
}
catch( std::exception& e )
{
std::cerr << "Error: " << e.what() << "\n";
return 0;
}
catch(...)
{
std::cerr << "Unkown error!\n";
return 0;
}
}
我的 Text.cpp
文件:
#include <iostream>
#include <string.h>
#include "Text.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
//Constructors
Text::Text(){}
Text::Text(SDL_Renderer *renderer, std::string file, int ptsize, std::string name)
{
set_renderer( renderer );
this->name = name;
set_TTF_Font( file, ptsize );
}
Text::~Text()
{
TTF_CloseFont( font );
SDL_DestroyTexture( texture );
}
void Text::set_renderer( SDL_Renderer *renderer )
{
if( renderer == NULL )
throw std::runtime_error( name + ": Renderer could not be set! renderer == NULL\n" + SDL_GetError() );
this->renderer = renderer;
}
void Text::set_TTF_Font( std::string file, int ptsize )
{
TTF_CloseFont( font );
font = NULL;
SDL_DestroyTexture( texture );
texture = NULL;
width = 0;
height = 0;
if( file == "" )
throw std::runtime_error( name + ": TTF_Font* could not be set! file == ""\n" + SDL_GetError() );
if( ptsize <= 0 )
throw std::runtime_error( name + ": TTF_Font* could not be set! ptsize <= 0\n" + SDL_GetError() );
TTF_Font *font = TTF_OpenFont( file.c_str(), ptsize );
if( font == NULL )
throw std::runtime_error( name + ": TTF_Font* could not be set! font == NULL\n" + SDL_GetError() );
this->font = font;
}
void Text::render_Text_Solid( std::string text, SDL_Color fg )
{
SDL_DestroyTexture( texture );
texture = NULL;
width = 0;
height = 0;
SDL_Surface *surface = TTF_RenderText_Solid( font, text.c_str(), fg );
if( surface == NULL )
throw std::runtime_error( name + ": Text could not be created! surface == NULL\n" + SDL_GetError() );
texture = SDL_CreateTextureFromSurface( renderer, surface );
width = surface->w;
height = surface->h;
SDL_FreeSurface( surface );
if( texture == NULL )
throw std::runtime_error( name + ": Text could not be created! texture == NULL\n" + SDL_GetError() );
}
我的 Text.h
文件:
#ifndef TEXT_H_INCLUDED
#define TEXT_H_INCLUDED
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
class Text
{
public:
//constructors
Text();
Text(SDL_Renderer *renderer, std::string file, int ptsize, std::string name);
~Text();
void set_renderer( SDL_Renderer *renderer );
void set_TTF_Font( std::string file, int ptsize );
void render_Text_Solid( std::string text, SDL_Color fg );
//render
void render( int x, int y );
void render( SDL_Rect *srcrect, SDL_Rect *dstrect );
//variables
int width = 0;
int height = 0;
private:
SDL_Renderer *renderer = NULL;
SDL_Texture *texture = NULL;
TTF_Font *font = NULL;
std::string name = "";
};
#endif // TEXT_H_INCLUDED
PS:我使用 Manjaro Linux 和 Codeblocks
我们先看看这几行是干什么的:
Text title;
title = Text( renderer, "cruft.ttf", 32, "title" );
首先创建 title
,并使用默认构造函数 Text::Text()
对其进行初始化,这会为其字段设置默认值。然后,您使用专门的构造函数创建第二个 Text
(为清楚起见,我们将其称为 title1
)对象。然后将 title1
复制到 title
- 由于未定义 operator=
,因此生成默认副本。现在您的 title
与 title1
具有相同的值。 title1.~Text()
被调用,杀死你的 font
.
你所拥有的是你的 title.font
仍然有它以前的值,但它指向已经关闭的字体结构。
这可以通过不构建临时 Text
对象来避免 - 例如Text title = Text(.......)
不会产生临时对象和复制。这只是一种解决方法,实际问题仍然存在 - 您的 Text
对象无法安全复制。有很多方法可以解决这个问题——比如使用某种 unique_ptr
包装器或放弃析构函数以支持手动 deinit 方法等。
这给我们带来了下一个问题 - 如果您解决了复制问题,您现在可以生成文本表面和纹理,但请查看您的退出代码:
Text title;
// ... omitted
SDL_Quit();
TTF_Quit();
return 0;
您的最终确定是相反的 - 您想要关闭字体、删除纹理、销毁 renderer/window、然后 调用 TTF_Quit
和 SDL_Quit
,而是你有 SDL_Quit()
、TTF_Quit()
,并且只有在那之后 title.~Text()
- 可能会崩溃,因为 SDL/TTF 已经完成并且你不应该执行 SDL在那之后打电话。当然这也可以解决,例如通过将您的 title
包含在额外的代码块中,但是要注意的东西的数量会变得太大。