首页 > 解决方案 > C ++:如何创建“条件”构造函数/避免在赋值时破坏?

问题描述

让我们从一些示例代码开始:


class Shader {
public:

    static absl::StatusOr<Shader> Create(const std::string &vertexShaderFile, const std::string &fragmentShaderFile) {
        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;
        glDeleteProgram(_programId);
    }

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

用法:

const auto &_statusOr4 = (Shader::Create("resources/vertex.glsl", "resources/frag.glsl"));
if (!_statusOr4.ok())return _statusOr4.status();
Shader shader = *_statusOr4;

std::cerr << "BBB" << std::endl;

这将打印:

AAA
destruct!
BBB

我认为sinShader::Create复制到一个新对象,然后旧对象被破坏,这调用了~Shader析构函数。

我想避免复制+破坏。

我不能将所有Create代码都扔到 c'tor 中,因为有一些操作可能会失败。

我怎样才能做到这一点?


添加

    Shader(const Shader&) = delete;
    Shader& operator=(const Shader&) = delete;

阻止该程序编译。这很好,因为这确实是一个错误,但是我仍然不知道如何编写这样的Create方法(没有将所有内容移入构造函数并引发异常)


我试图添加一个移动构造函数

    Shader(Shader&& o) noexcept : _programId(o._programId){
        std::cerr << "move ctor" << std::endl;
    }

必须更改作业以使其成为参考:

const auto &_statusOr4 = (Shader::Create("resources/vertex.glsl", "resources/frag.glsl"));
if (!_statusOr4.ok()) return _statusOr4.status();
const Shader& shader = *_statusOr4;

我认为应该没问题,因为_statusOr4它们的寿命相同。

但是....现在我明白了

AAA
move ctor
destruct!
BBB

似乎析构函数仍然在它被“移出”的对象上被调用。


StatusOr来自Abseil

标签: c++

解决方案


我想避免复制+破坏。

通常,当您的函数返回类型与您返回的变量类型匹配时,您无需执行任何操作。没有复制,也没有破坏发生。阅读有关复制省略的内容。

在您的情况下,您构造Shader但返回absl::StatusOr<Shader>。所以有两个不同的对象:s函数中的局部变量和返回值。它们属于不同的类型,因此编译器无法使它们成为相同的东西,您将看到s被调用的析构函数。但是,如果您Shader正确编写,这是无害的。支持移动构造,并且s会被移动到返回值,留下一个容易破坏的空壳。仍然没有复制

假设 0 是无效/空着色器程序并且glDeleteProgram(0)本质上是无操作的,则移动构造函数可能如下所示:

Shader(Shader&& other) {
   _programId = other._programId;
   other._programId = 0;
}

您不想随意复制对象Shaderobjects,否则您最终会 _programId多次删除相同的对象。为避免这种情况,请将复制构造函数和复制赋值运算符标记为delete.

我不能把所有的 Create 代码都扔到 c'tor 中,因为有一些操作可能会失败。

这就是例外的原因。


推荐阅读