如何从函数中正确返回SFML的sf::Text对象?
问题描述
我编写了一个用于在SFML中显示文本的类,希望通过函数返回文本对象,以便在main函数中使用oknoAplikacji.draw(wyjdz_z_gry.wyswietlanie())(其中oknoAplikacji是主窗口,wyjdz_z_gry是该类的实例)。
最初尝试返回sf::Text对象:
sf::Text wyswietlanie(){ sf::Text napis; // 设置文本内容、字体等属性 return napis; }
但该方法无效。随后改为返回指针:
sf::Text * wyswietlanie(){ sf::Text napis; // 设置napis的属性 return &napis; }
并在主循环中调用oknoAplikacji.draw(*wyjdz_z_gry.wyswietlanie());,代码可编译但运行后立即崩溃,错误信息为:
pure virtual method called terminate called without an active exception
完整代码如下:
#include <SFML/Window.hpp> #include <iostream> #include <SFML/Graphics.hpp> using namespace std; class Przycisk{ public: string nazwa; int wymiarx; int wymiary; int pozycjax; int pozycjay; Przycisk(string nazwa, int wymiarx, int wymiary, int pozycjax, int pozycjay){ this->nazwa=nazwa; this->wymiary =wymiary; this->wymiary=wymiary; this->pozycjax=pozycjax; this->pozycjay=pozycjay; } sf::Text * wyswietlanie(){ sf::Text napis; sf::Font czcionka; czcionka.loadFromFile("czcionki/arial.ttf"); napis.setFont(czcionka); napis.setString(this->nazwa); napis.setPosition(this->pozycjax, this->pozycjay); return &napis; } }; int main() { int szerokosc = sf::VideoMode::getDesktopMode().width; int wysokosc = sf::VideoMode::getDesktopMode().height; sf::RenderWindow oknoAplikacji( sf::VideoMode(szerokosc, wysokosc, 32), "nazwa okna", sf::Style::Default); sf::Texture menu_glowne_tlo; menu_glowne_tlo.loadFromFile( "tekstury/menuwersja_na_komputer_szkolny.png" ); sf::Sprite menu_glowne; menu_glowne.setTexture(menu_glowne_tlo); Przycisk wyjdz_z_gry("wyjdz z gry", 280, 60, 800, 250); while( oknoAplikacji.isOpen() ) { sf::Event zdarzenie; while( oknoAplikacji.pollEvent( zdarzenie ) ) { if( zdarzenie.type == sf::Event::Closed ) oknoAplikacji.close(); if( zdarzenie.type == sf::Event::KeyPressed && zdarzenie.key.code == sf::Keyboard::Escape ) oknoAplikacji.close(); } oknoAplikacji.draw(menu_glowne); oknoAplikacji.draw(*wyjdz_z_gry.wyswietlanie()); oknoAplikacji.display(); } return 0; }
注:变量使用波兰语是为了便于阅读,技术术语均使用英文。
错误原因分析
返回
sf::Text对象无效的原因:wyswietlanie()函数中的sf::Font czcionka是局部变量,函数执行完毕后会被销毁。而sf::Text仅持有字体的引用,并非复制字体数据,当字体被销毁后,文本对象失去有效依赖,无法正常渲染。返回指针崩溃的原因:
返回的是局部变量napis的地址,函数执行完毕后napis会被销毁,此时指针变为野指针。主函数中解引用该指针访问已销毁的对象,触发未定义行为,导致程序崩溃,出现“pure virtual method called”错误(通常是访问已销毁的多态对象时触发)。
正确解决方案
核心要点:保证sf::Font的生命周期至少和sf::Text一致,同时避免返回局部对象的指针/引用。推荐将字体和文本作为类的成员变量,初始化时仅加载一次,既能解决生命周期问题,又能提升性能。
修改后的代码:
#include <SFML/Window.hpp> #include <iostream> #include <SFML/Graphics.hpp> using namespace std; class Przycisk{ public: string nazwa; int wymiarx; int wymiary; int pozycjax; int pozycjay; // 将字体和文本设为类成员,确保生命周期与对象一致 sf::Font czcionka; sf::Text napis; // 构造函数中初始化字体和文本 Przycisk(string nazwa, int wymiarx, int wymiary, int pozycjax, int pozycjay){ this->nazwa = nazwa; this->wymiarx = wymiarx; this->wymiary = wymiary; this->pozycjax = pozycjax; this->pozycjay = pozycjay; // 加载字体(只需加载一次) if(!czcionka.loadFromFile("czcionki/arial.ttf")){ cerr << "Nie można załadować czcionki!" << endl; exit(EXIT_FAILURE); } // 初始化文本属性 napis.setFont(czcionka); napis.setString(this->nazwa); napis.setPosition(this->pozycjax, this->pozycjay); // 可根据按钮尺寸调整文本大小 napis.setCharacterSize(24); } // 返回文本对象的引用(避免复制,提升性能) sf::Text& wyswietlanie(){ return napis; } }; int main() { int szerokosc = sf::VideoMode::getDesktopMode().width; int wysokosc = sf::VideoMode::getDesktopMode().height; sf::RenderWindow oknoAplikacji( sf::VideoMode(szerokosc, wysokosc, 32), "nazwa okna", sf::Style::Default); sf::Texture menu_glowne_tlo; if(!menu_glowne_tlo.loadFromFile( "tekstury/menuwersja_na_komputer_szkolny.png" )){ cerr << "Nie można załadować tła!" << endl; exit(EXIT_FAILURE); } sf::Sprite menu_glowne; menu_glowne.setTexture(menu_glowne_tlo); Przycisk wyjdz_z_gry("wyjdz z gry", 280, 60, 800, 250); while( oknoAplikacji.isOpen() ) { sf::Event zdarzenie; while( oknoAplikacji.pollEvent( zdarzenie ) ) { if( zdarzenie.type == sf::Event::Closed ) oknoAplikacji.close(); if( zdarzenie.type == sf::Event::KeyPressed && zdarzenie.key.code == sf::Keyboard::Escape ) oknoAplikacji.close(); } oknoAplikacji.clear(); // 绘制前清空窗口,避免帧残留 oknoAplikacji.draw(menu_glowne); oknoAplikacji.draw(wyjdz_z_gry.wyswietlanie()); oknoAplikacji.display(); } return 0; }
关键改进点:
- 将
sf::Font和sf::Text设为类成员,确保它们的生命周期与Przycisk实例一致,避免局部变量销毁导致的问题。 - 在构造函数中仅加载一次字体,避免每次调用
wyswietlanie()时重复加载(字体加载是性能开销较大的操作)。 - 返回
sf::Text的引用,避免对象复制,提升性能,同时主窗口可直接使用该引用进行绘制。 - 添加资源加载失败的错误处理,便于调试。
- 主循环中添加
oknoAplikacji.clear(),解决帧残留问题。
内容的提问来源于stack exchange,提问作者antykwariat
相关产品推荐
相关产品推荐

