在游戏引擎布局中使用共享的ptr? 我在C++中编写了一个小游戏引擎,它运行正常,但不是最好的性能。我也学到了很多,现在我想重做。但上一次,我大量使用shared_ptr作为类,比如gui纹理和模型包装器,它们保持3d位置和opengl vao。我听说不应该真正使用shared_ptr,但我真的不知道如何用其他方法。以下是类似布局的伪代码:

在游戏引擎布局中使用共享的ptr? 我在C++中编写了一个小游戏引擎,它运行正常,但不是最好的性能。我也学到了很多,现在我想重做。但上一次,我大量使用shared_ptr作为类,比如gui纹理和模型包装器,它们保持3d位置和opengl vao。我听说不应该真正使用shared_ptr,但我真的不知道如何用其他方法。以下是类似布局的伪代码:,c++,game-engine,shared-ptr,C++,Game Engine,Shared Ptr,这段代码不完整或不起作用,但我想分享我的布局想法 // d3 stands for 3d class D3Model { VAO vao; bool useBloom; unsigned int texture; // ... and so on static std::vector<std::shared_ptr<D3Model>> models; // current idea, approach 1 static s

这段代码不完整或不起作用,但我想分享我的布局想法

// d3 stands for 3d
class D3Model {
    VAO vao;
    bool useBloom;
    unsigned int texture;
    // ... and so on

    static std::vector<std::shared_ptr<D3Model>> models; // current idea, approach 1
    static std::vector<D3Model> models1; // different approach (2)

    D3Model() {
        models.push_back(shared_from_this()); // app.1
        models1.push_back(*this);
    }
}


// main file
int main() {
    std::shared_ptr<D3Model> model = std::make_shared<D3Model>();
    model->setTexture(0); // ignore the non sense content

    D3Model model1;
    model1.setTexture(0); // would not get updated in the vector

    while(true) {
        model->increaseRotation(1);
        model1.increaseRotation(1);
        Renderer::render();
    }
}

// different file
class Renderer {
    static void render() {
        for(const auto& all : D3Model::models) {
            // render model
        }

        for(const auto& all : D3Model::models1) {
            // render model1, would not have increased rotation
        }
    }
}

要了解更多信息,请想象发动机的以下用法: 在开始屏幕上点击play后,引擎从文件加载一些3d模型并存储它们。其中一个每帧旋转一次,每个帧渲染一次。由于玩家加入的游戏性发生了变化,可能之后必须加载其他一些模型。。。。然后,当用户返回主菜单时,它们都会被删除。方法2的问题是,每当模型旋转增加时,我都必须更新向量,而方法1会自动处理。然而,我听说共享ptr对性能不利

在这种情况下,shared_ptr是最好的解决方案还是应该只使用普通对象?但我的问题是如何将更改应用到对象(例如setTexture),而不必更新向量内容。原始指针也不是很好。这样的事情是怎么做的?
谢谢,虚幻

好的,你可以像我一样创建自己的refconner类:

/// RefCounter.hpp
#pragma once
class RefCounter {
public:

    RefCounter();

    RefCounter(const RefCounter&);

    RefCounter& operator=(const RefCounter&);

    void grab();

    usize reference() const;

    bool release();

protected:

    virtual void finalize();

    virtual ~RefCounter();

private:

    usize _reference;

};


template<typename T>
struct ref {

    inline ref() : class_ptr (nullptr) {}

    inline ref(T* obj) : class_ptr (obj) { if(class_ptr) class_ptr->grab(); }

    inline ref(const ref& other) : class_ptr (other.class_ptr) { if(class_ptr) class_ptr->grab(); }

    inline ref& operator=(T* obj) {
        if(obj)
            obj->grab();
        if(class_ptr)
            class_ptr->release();
        class_ptr = obj;
        return *this;
    }

    inline ref& operator=(const ref& other){
        T* obj = other.class_ptr;
        if(obj)
            obj->grab();
        if(class_ptr)
            class_ptr->release();
        class_ptr = obj;
        return *this;
    }

    T* get() const {
        return class_ptr;
    }

    operator T*() const {
        return class_ptr;
    }

    T* operator->() const {
        return class_ptr;
    }

    inline ~ref() {
        if(class_ptr)
            class_ptr->release();
    }

private:
    T* class_ptr;
};

/// RefCounter.cpp


RefCounter::RefCounter() : _reference(0){

}

RefCounter::RefCounter(const RefCounter&) : _reference(0) {

}

RefCounter& RefCounter::operator=(const RefCounter&) {
    return *this;
}

void RefCounter::grab() {
    _reference++;
}

usize RefCounter::reference() const {
    return _reference;
}

bool RefCounter::release() {
    if(_reference > 1) {
        _reference--;
        return false;
    } else {
        _reference = 0;
        finalize();
        return true;
    }
}

void RefCounter::finalize() {
    delete this;
}

RefCounter::~RefCounter() {
}

好的,您可以像我一样创建自己的refconner类:

/// RefCounter.hpp
#pragma once
class RefCounter {
public:

    RefCounter();

    RefCounter(const RefCounter&);

    RefCounter& operator=(const RefCounter&);

    void grab();

    usize reference() const;

    bool release();

protected:

    virtual void finalize();

    virtual ~RefCounter();

private:

    usize _reference;

};


template<typename T>
struct ref {

    inline ref() : class_ptr (nullptr) {}

    inline ref(T* obj) : class_ptr (obj) { if(class_ptr) class_ptr->grab(); }

    inline ref(const ref& other) : class_ptr (other.class_ptr) { if(class_ptr) class_ptr->grab(); }

    inline ref& operator=(T* obj) {
        if(obj)
            obj->grab();
        if(class_ptr)
            class_ptr->release();
        class_ptr = obj;
        return *this;
    }

    inline ref& operator=(const ref& other){
        T* obj = other.class_ptr;
        if(obj)
            obj->grab();
        if(class_ptr)
            class_ptr->release();
        class_ptr = obj;
        return *this;
    }

    T* get() const {
        return class_ptr;
    }

    operator T*() const {
        return class_ptr;
    }

    T* operator->() const {
        return class_ptr;
    }

    inline ~ref() {
        if(class_ptr)
            class_ptr->release();
    }

private:
    T* class_ptr;
};

/// RefCounter.cpp


RefCounter::RefCounter() : _reference(0){

}

RefCounter::RefCounter(const RefCounter&) : _reference(0) {

}

RefCounter& RefCounter::operator=(const RefCounter&) {
    return *this;
}

void RefCounter::grab() {
    _reference++;
}

usize RefCounter::reference() const {
    return _reference;
}

bool RefCounter::release() {
    if(_reference > 1) {
        _reference--;
        return false;
    } else {
        _reference = 0;
        finalize();
        return true;
    }
}

void RefCounter::finalize() {
    delete this;
}

RefCounter::~RefCounter() {
}

当您需要共享所有权管理时,请使用std::shared_ptr。如果是静态std::向量模型;如果工作正常,那么您可能一开始就不需要共享所有权管理。不过,如果模型需要随着时间的推移而增长,那么D3Model的指针和引用将无法保持有效,这是您在使用std::shared_ptr时不必担心的问题。所以它可能不是最好的容器。我还没有实现向量版本方法2,但我只能在每次我更改其中一个模型中的任何内容时更新向量,这显然不会很快通过某种id在向量中找到类似的对象,删除它并推送新版本。当世界加载时,模型可能会有一个大的增长,即使它应该能够在任何给定的时间加载模型以获得更灵活的使用。如果你想让它在任何时候增长,你必须假设它在任何时候都会增长。因此,您可能存在无效问题。我不明白为什么需要删除并重新添加元素来更改它们。向量是一个可变的容器,您可以直接更改元素。通过索引在向量中查找元素的时间是恒定的,开销很小。一点也不慢。这段代码不应该处理方法渲染中m的纹理为零的问题,对吗?这就是我所说的[Edit:添加了一个updateInVector方法:,其中m的纹理为1,但这是最有效的方法吗?]看起来您正在尝试自动维护实例列表。这种方法可以工作,但充满了陷阱,通常不值得付出努力。至少您需要模型来存储指向D3Model的指针。现在,每次创建一个D3模型时,都会在无限循环中将另一个模型插入到模型中。您还将遇到一个问题,即当D3Model被销毁时(例如,如果m在另一个函数中),您将有一个指向该实例的悬空指针。更不用说复制和移动构造函数和操作符了。当您需要共享所有权管理时,请使用std::shared_ptr。如果是静态std::向量模型;如果工作正常,那么您可能一开始就不需要共享所有权管理。不过,如果模型需要随着时间的推移而增长,那么D3Model的指针和引用将无法保持有效,这是您在使用std::shared_ptr时不必担心的问题。所以它可能不是最好的容器。我还没有实现向量版本方法2,但我只能在每次我更改其中一个模型中的任何内容时更新向量,这显然不会很快通过某种id在向量中找到类似的对象,删除它并推送新版本。当世界加载时,模型可能会有一个大的增长,即使它应该能够在任何给定的时间加载模型以获得更灵活的使用。如果你想让它在任何时候增长,你必须假设它在任何时候都会增长。因此,您可能存在无效问题。我不明白为什么需要删除并重新添加元素来更改它们。向量是一个可变的容器,您可以直接更改元素。通过索引在向量中查找元素的时间是恒定的,开销很小。一点也不慢。这段代码不应该处理以下问题:在
方法渲染,对吗?这就是我所说的[Edit:添加了一个updateInVector方法:,其中m的纹理为1,但这是最有效的方法吗?]看起来您正在尝试自动维护实例列表。这种方法可以工作,但充满了陷阱,通常不值得付出努力。至少您需要模型来存储指向D3Model的指针。现在,每次创建一个D3模型时,都会在无限循环中将另一个模型插入到模型中。您还将遇到一个问题,即当D3Model被销毁时(例如,如果m在另一个函数中),您将有一个指向该实例的悬空指针。更不用说复制和移动构造函数和运算符了。我看不出这对解决原始问题有什么帮助。那么也许你应该解释一下为什么std::shared_ptr不可接受,为什么这个解决方案更好,以及如何使用它。这个答案似乎和现在一样不完整。请注意,似乎只有从RefCounter派生或实现等效接口的对象才能被ref引用,这是一种侵入性设计。例如,如果没有额外的包装器,引用外部库中的类型是不可行的。它似乎也不支持移动语义。它表明,这种方法当然可以工作,但它确实有缺点,包括我提到的缺点。但我清楚地记得公开抓取和释放是多么烦人。如果我没有弄错的话,这个引擎早于c++11,c++11引入了标准的智能指针和移动语义。编辑:第一次发布是在2006年。我不认为这对解决最初的问题有什么帮助。那么也许你应该解释一下为什么std::shared_ptr不可接受,为什么这个解决方案更好,以及如何使用它。这个答案似乎和现在一样不完整。请注意,似乎只有从RefCounter派生或实现等效接口的对象才能被ref引用,这是一种侵入性设计。例如,如果没有额外的包装器,引用外部库中的类型是不可行的。它似乎也不支持移动语义。它表明,这种方法当然可以工作,但它确实有缺点,包括我提到的缺点。但我清楚地记得公开抓取和释放是多么烦人。如果我没有弄错的话,这个引擎早于c++11,c++11引入了标准的智能指针和移动语义。编辑:第一次发布是在2006年。