c++ 我应该在这里使用std::vector还是std::unique_ptr?

cbwuti44  于 2023-02-10  发布在  其他
关注(0)|答案(1)|浏览(135)

我有一个Mesh类,我想拥有verticesindicesvertex_normals等几个不同的“类似数组”结构的所有权。
我希望Mesh类完全独立于这些格式的源代码,因为我需要支持许多3d格式,因此我目前有一组加载器函数,可以从特定格式读取3d数据,创建网格,并将其分配给Model的示例(包含unique_ptr到特定Mesh)。
我目前的做法是在loader函数中简单地堆分配数组,然后将指向这些数组的指针传递给Mesh构造函数。但看起来使用std::vectorstd::unique_ptr会更安全。一个loader函数(特别是从OBJ文件加载的函数)的快速模型如下所示:

void load_OBJ(std::shared_ptr<Model> model, std::string file_path){
    # Read the 3d data
    ...

    # Create the Mesh object
    std::unique_ptr<Mesh> mesh(new Mesh());

    # Add the 3d data to the Mesh (???)
    mesh->set_vertices( ??? );
    mesh->set_indices( ??? );
    ...

    # Assign the Mesh object to the Model:
    model->set_mesh(mesh);
};

我的第一个直觉React是,因为Mesh将保持对其所有成员的独占所有权,所以我应该使用std::unique_ptr,所以类似于以下内容:

class Mesh {
    public:
        std::unique_ptr<Vector3[]> vertices;
        uint32_t num_vertices;

        Mesh() { };
        ~Mesh() { };

        void set_vertices(std::unique_ptr<Vector3[]>& new_vertices, uint32_t num_vertices){
            this->vertices = std::move(new_vertices);
            this->num_vertices = num_vertices;
        };

但后来我开始怀疑std::vector是否是更好的方法。在这种情况下,能够从Mesh访问数据的速度将是极其重要的。(使用编译器优化)提示我,从std::unique_ptr<Vector3[]>std::vector<Vector3>访问数据实际上是无法区分的。我不知道这里是否还有什么我遗漏的东西需要考虑?

pbpqsu0x

pbpqsu0x1#

std::vector为您提供了对动态大小的元素数组的独占所有权,这正是您想要做的。它还通过允许您保存容易出错的显式大小变量来简化事情。
使用向量也不会引入太多开销:它只是一个指向某个内存的指针,加上了sizecapacity(实际上,它们也被实现为指针,所以一个向量就是三个指针)。
如果你要把push_back导入到vector中,请确保先用合适的大小调用reserve

vertices.reserve(numVertices);
for (size_t i = 0; i < numVertices; ++i) {
    // load vertex
    vertices.push_back(vertex);
}

否则,向量的默认增长策略可能会使其大于您所需要的大小,从而浪费内存(如果我没记错的话,每当分配给向量的存储空间变满时,向量的大小默认会翻倍)。
如果你想把memcpy转换成向量,你必须先给resize它分配一个适当的存储量。
要传递向量而不进行复制,请使用std::move。它可以归结为与unique_ptr相同的操作--只是移动几个指针。您可以在需要时轻松复制向量,但不是必须这样做。
如果你想让一个函数或构造函数“消费”一个向量(取得它的所有权),只需要按值取它,然后把它移到它的最终目的地:

void Model::setMesh(std::vector<Vertex> vertices) {
    m_mesh = std::move(vertices);
}

std::vector<Vertex> v = ...;
model->setMesh(std::move(v));

如果你用一个临时对象(或移动对象)调用函数,则不会在那里进行复制;但是如果你用左值调用它,它会进行复制并保持原始对象不变,这意味着你可以选择是复制还是移动对象到函数中。

相关问题