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

C++如何实现条件构造/避免赋值时析构且不使用构造函数抛异常?

你当前实现的移动构造逻辑存在缺陷:仅完成了资源句柄的复制,没有清空原对象持有的句柄,导致原对象析构时直接释放了已经转移给新对象的GPU程序资源。

解决方案

第一步:完善资源管理逻辑

遵循RAII规则,修改析构函数、实现完整的移动语义,禁止拷贝语义:

class Shader {
public:
    static absl::StatusOr<Shader> Create(const std::string &vertexShaderFile, const std::string &fragmentShaderFile) {
        // 原有Create逻辑保持不变
        ASSIGN_OR_RETURN(GLuint vertexShader, gl::createShader(GL_VERTEX_SHADER, readFile(vertexShaderFile)))
        ASSIGN_OR_RETURN(GLuint fragmentShader, gl::createShader(GL_FRAGMENT_SHADER, readFile(fragmentShaderFile)))

        Shader s;
        glAttachShader(s._programId, vertexShader);
        glAttachShader(s._programId, fragmentShader);
        RETURN_IF_ERROR(gl::linkProgram(s._programId));
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        std::cerr << "AAA" << std::endl;
        return s;
    }

    void use() const {
        std::cerr << "use " << _programId << std::endl;
        glUseProgram(_programId);
    }

    ~Shader() {
        std::cerr << "destruct!" << std::endl;
        // 仅有效句柄才释放资源
        if (_programId != 0) {
            glDeleteProgram(_programId);
        }
    }

    // 禁止拷贝
    Shader(const Shader&) = delete;
    Shader& operator=(const Shader&) = delete;

    // 实现移动语义
    Shader(Shader&& o) noexcept : _programId(o._programId) {
        std::cerr << "move ctor" << std::endl;
        // 清空原对象的句柄,避免原对象析构释放已转移的资源
        o._programId = 0;
    }

    Shader& operator=(Shader&& o) noexcept {
        std::cerr << "move assign" << std::endl;
        if (this != &o) {
            // 先释放自身持有的资源
            if (_programId != 0) {
                glDeleteProgram(_programId);
            }
            // 转移资源
            _programId = o._programId;
            o._programId = 0;
        }
        return *this;
    }

private:
    explicit Shader() : _programId(glCreateProgram()) {}
    GLuint _programId{0};
};

第二步:调整使用方式

不需要强制持有引用,直接接收移动后的对象即可:

absl::StatusOr<Shader> statusOr = Shader::Create("resources/vertex.glsl", "resources/frag.glsl");
if (!statusOr.ok()) {
    return statusOr.status();
}
// 从StatusOr中移动构造对象
Shader shader = *std::move(statusOr);

std::cerr << "BBB" << std::endl;
// shader离开作用域时才会真正释放GPU程序资源

说明

  • 此时输出中的第一次destruct!是被移动后的原对象析构触发,因为原对象的_programId已经被置为0,不会实际删除GPU程序,不会影响你持有的shader对象的有效性。
  • 编译器开启返回值优化(RVO)的场景下,会直接在返回的StatusOr内存空间中构造Shader对象,连移动构造的开销都会被完全消除。
  • 最终你持有的shader对象离开作用域时,才会触发析构释放GPU资源,符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 14:45:07