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

SFML中使用vector::emplace_back添加带Font成员的TextButton触发段错误

问题原因与解决方案

核心问题分析

  • SFML的sf::Text调用setFont()时,不会复制字体数据,仅保存一个指向sf::Font对象的引用。
  • 当用emplace_back()添加第二个按钮时,std::vector因容量不足触发内存扩容:它会在新内存构造新对象,将旧内存中的TextButton移动到新位置后销毁旧对象。
  • 移动后的TextButton中,sf::Text仍指向旧内存中已销毁的sf::Font成员,调用window.draw(text)时访问悬空引用,触发段错误。
  • 而push_back()场景下,先创建独立对象再拷贝,每个拷贝的TextButton都有独立的sf::Font,sf::Text指向自身的font成员,无悬空问题;单个emplace_back时vector无需扩容,没有移动操作,因此正常运行。

解决方案

方案1:用智能指针共享字体(推荐,同时优化性能)

每个按钮重复加载字体冗余且低效,使用std::shared_ptr<sf::Font>让所有按钮共享同一字体实例,彻底解决引用悬空:

#include <memory>

class TextButton {
    public:
    using Action = void(*)();

    private:
    sf::Vector2f position;
    sf::Vector2f size;
    Action action;
    
    sf::RectangleShape shape;
    sf::Text text;
    std::shared_ptr<sf::Font> font; // 替换原sf::Font成员

    public:
    // 保持原有成员函数声明不变
    TextButton(const sf::Vector2f& position, const sf::Vector2f& size, const sf::Color& color,
               const unsigned char* binaryFont, const unsigned int binaryFontSizeInBytes,
               const int characterSize, const sf::Color& textColor, const std::string& label, Action action);

    void draw(sf::RenderWindow& window) const;
    bool is_clicked(const sf::Vector2f& mousePosition) const;
    void on_click() const;
};

// 构造函数实现修改
TextButton::TextButton(const sf::Vector2f& position, const sf::Vector2f& size, const sf::Color& color,
                       const unsigned char* binaryFont, const unsigned int binaryFontSizeInBytes,
                       const int characterSize, const sf::Color& textColor, const std::string& label, Action action) 
: position(position), size(size), action(action),
  font(std::make_shared<sf::Font>()) {
    shape.setSize(size);
    shape.setPosition(position);
    shape.setFillColor(color);

    font->loadFromMemory(binaryFont, binaryFontSizeInBytes);
    text.setFont(*font); // 绑定到智能指针指向的字体
    text.setString(label);
    text.setCharacterSize(characterSize);
    text.setFillColor(textColor);

    sf::FloatRect textBounds = text.getLocalBounds();
    text.setOrigin(textBounds.left + textBounds.width/2.f, textBounds.top + textBounds.height/2.f);
    text.setPosition(position.x + shape.getSize().x/2.f, position.y + shape.getSize().y/2.f);
}

// 其余成员函数实现保持不变

方案2:手动实现拷贝/移动构造函数

若坚持每个按钮独立持有字体,需手动实现拷贝、移动构造及赋值运算符,确保sf::Text始终指向当前对象的font成员:

class TextButton {
    // 原有成员声明不变
public:
    // 拷贝构造
    TextButton(const TextButton& other) 
    : position(other.position), size(other.size), action(other.action),
      shape(other.shape), text(other.text), font(other.font) {
        text.setFont(font); // 重新绑定到当前对象的font
    }

    // 拷贝赋值
    TextButton& operator=(const TextButton& other) {
        if (this != &other) {
            position = other.position;
            size = other.size;
            action = other.action;
            shape = other.shape;
            font = other.font;
            text = other.text;
            text.setFont(font);
        }
        return *this;
    }

    // 移动构造
    TextButton(TextButton&& other) noexcept
    : position(std::move(other.position)), size(std::move(other.size)), action(std::move(other.action)),
      shape(std::move(other.shape)), text(std::move(other.text)), font(std::move(other.font)) {
        text.setFont(font);
    }

    // 移动赋值
    TextButton& operator=(TextButton&& other) noexcept {
        if (this != &other) {
            position = std::move(other.position);
            size = std::move(other.size);
            action = std::move(other.action);
            shape = std::move(other.shape);
            font = std::move(other.font);
            text = std::move(other.text);
            text.setFont(font);
        }
        return *this;
    }

    // 原有成员函数声明不变
};

方案3:提前预留vector容量(临时规避)

提前给vector分配足够容量,避免扩容时的移动操作,但仅能临时解决问题,后续添加更多按钮仍可能触发错误:

std::vector<TextButton> buttons;
buttons.reserve(2); // 提前预留2个元素的容量
buttons.emplace_back(sf::Vector2f(200.f, 200.f), sf::Vector2f(300.f, 80.f), sf::Color::Green, low_bit_pixel_ttf, low_bit_pixel_ttf_len, 30, sf::Color::Black, "button 1", []{ std::cout << "first button pressed\n"; });
buttons.emplace_back(sf::Vector2f(500.f, 350.f), sf::Vector2f(200.f, 60.f), sf::Color::Red, low_bit_pixel_ttf, low_bit_pixel_ttf_len, 30, sf::Color::White, "button 2", []{ std::cout << "second button pressed\n"; });

内容的提问来源于stack exchange,提问作者user21766581

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 07:57:30