You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从函数中正确返回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;
}

注:变量使用波兰语是为了便于阅读,技术术语均使用英文。


错误原因分析
  1. 返回sf::Text对象无效的原因:
    wyswietlanie()函数中的sf::Font czcionka是局部变量,函数执行完毕后会被销毁。而sf::Text仅持有字体的引用,并非复制字体数据,当字体被销毁后,文本对象失去有效依赖,无法正常渲染。

  2. 返回指针崩溃的原因:
    返回的是局部变量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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 19:01:19