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
相关产品推荐
相关产品推荐

