I have a Mesh class that I want to have ownership over several different "array-like" structures for vertices,indices,vertex_normals,etc.
I would like for the Mesh class to be totally independent of the source of these formats, as I have many 3d formats I need to support. As such I currently have a set of loader functions that read the 3d data from a specific format, create a mesh, and assign it to an instance of a Model (which contains a unique_ptr to a specific Mesh).
I’m currently doing this by simply heap allocating arrays inside the loader function, then passing the pointers to those arrays into the Mesh constructor. But it seems like using either std::vector or std::unique_ptr would be a safer option. A quick mockup of what one of the loader functions (specifically one meant to load from an OBJ file) might look like is as follows:
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);
};
My first gut reaction was, because the Mesh will maintain exclusive ownership over all of its members, that I should use std::unique_ptr, so something like this:
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;
};
But then I started to wonder if perhaps std::vector is a better way to go. In this context, the speed of being able to access the data from Mesh is going to be extremely important. My rough benchmarks (with compiler optimizations) hint to me though that accessing data from either std::unique_ptr<Vector3[]> and std::vector<Vector3> are virtually indistinguishable. So I’m not sure if there is anything else to take into consideration here that I am missing?
>Solution :
A std::vector gives you exclusive ownership over a dynamically sized array of elements, which is exactly what you want to do. It simplifies things as well by allowing you to save on the error-prone explicit size variable. I’d say go for it.
Using a vector won’t introduce much overhead either: It’s just a pointer to some memory with an added size and capacity. (In practice, these are also implemented as pointers, so a vector is just three pointers.)
Make sure to call reserve with the appropriate size first if you’re going to push_back into the vector:
vertices.reserve(numVertices);
for (size_t i = 0; i < numVertices; ++i) {
// load vertex
vertices.push_back(vertex);
}
Otherwise, the vector’s default growth policy might make it bigger than you need it to be, wasting memory. (If I recall correctly, a vector doubles in size by default whenever its allocated storage becomes full.)
If you want to memcpy into the vector, you have to resize it first to allocate an appropriate amount of storage.
To pass the vector around without making copies, use std::move. It’ll boil down to the same operations as your unique_ptr – just shuffling a couple of pointers around. You can copy the vector easily when you want to, but you don’t have to.
If you want to make a function or constructor that "consumes" a vector (takes ownership of it), just take it by value and move it to its final destination:
void Model::setMesh(std::vector<Vertex> vertices) {
m_mesh = std::move(vertices);
}
std::vector<Vertex> v = ...;
model->setMesh(std::move(v));
No copy is ever made there if you call the function with a temporary (or moved object). If you call it with an lvalue, though, it’ll make a copy and leave the original object intact. This means that you can choose whether to copy or move something into the function.