我對 c++ 容器的使用印象就是容器中最好存對象的指針,不要直接存放對象。看下面的代碼片斷,最好用 vec1 ,而 vec2 在添加對象到容器中時,會多一次複製。c++
struct container { int a; int b; }; std::vector<container*> vec1; std::vector<container> vec2;
因此我潛意識就是存放指針,不要直接存放對象。而後在實際開發中,有些不那麼重要能知足需求的功能要是能採用更簡潔的代碼來實現,那就是太好了。上例中 vec1 在釋放內存時還須要遍歷 vector 釋放元素指向的對象,而 vec2 則沒必要,代碼就簡潔一部分。數組
我在看 opengl 代碼時看見一個例子。opengl 能夠調用 glDrawArrays
繪製,也能夠調用 glDrawElements
繪製,前者直接傳入頂點數組數據。後者傳入的是索引,如有相同的頂點數據則它們的索引是相同的,就有沒必要傳送相同數據的效果。我看見的代碼就是如何從一坨頂點數據中找到相同的頂點數據,並生成一個索引數組。舉個例子如有 4 個頂點,座標分別爲 (0, 0, 0), (1, 1, 1), (0, 0, 0), (2, 2, 2) 。使用索引時頂點數據就簡化成 (0, 0, 0), (1, 1, 1), (2, 2, 2) 而頂點索引數據則是 {0, 1, 2, 0} 。簡單來講就是找出相同的頂點數據。this
下面把那個 opengl 例子簡化了,只關注如何找出相同的頂點數據。具體代碼以下。指針
#include <cstdio> #include <cstring> #include <map> #include <string> #include <vector> struct vec3 { float x; float y; float z; }; struct vec2 { float x; float y; }; struct PackedVertex { vec3 position; vec2 uv; // 最妙的地方。 bool operator<(const PackedVertex that) const { return memcmp((void*)this, (void*)&that, sizeof(PackedVertex)) > 0; } std::string str() const { char buff[100]; snprintf(buff, 100, "(%f,%f,%f)(%f,%f)", position.x, position.y, position.z, uv.x, uv.y); std::string str(buff); return str; } }; short get_index( PackedVertex &packed, std::map<PackedVertex, short> &vertex_to_index) { std::map<PackedVertex, short>::iterator it = vertex_to_index.find(packed); if (it == vertex_to_index.end()) { return -1; } else { return it->second; } } vec3 fill_vec3(float x, float y, float z) { vec3 v3 = {x, y, z}; return v3; } vec2 fill_vec2(float x, float y) { vec2 v2 = {x, y}; return v2; } int main() { std::vector<vec3> vertices; std::vector<vec2> uvs; vertices.push_back(fill_vec3(1, 1, 1)); // [0] uvs.push_back(fill_vec2(0.1, 0.1)); vertices.push_back(fill_vec3(2, 2, 2)); // [1] uvs.push_back(fill_vec2(0.2, 0.2)); vertices.push_back(fill_vec3(3, 3, 3)); // [2] uvs.push_back(fill_vec2(0.3, 0.3)); vertices.push_back(fill_vec3(2, 2, 2)); uvs.push_back(fill_vec2(0.2, 0.2)); vertices.push_back(fill_vec3(1, 1, 1)); uvs.push_back(fill_vec2(0.1, 0.1)); vertices.push_back(fill_vec3(4, 4, 4)); // [3] uvs.push_back(fill_vec2(0.4, 0.4)); vertices.push_back(fill_vec3(4, 4, 4)); uvs.push_back(fill_vec2(0.4, 0.4)); std::map<PackedVertex, short> vertex_to_index; short index; for (unsigned int i = 0; i < vertices.size(); i++) { PackedVertex packed = {vertices[i], uvs[i]}; index = get_index(packed, vertex_to_index); if (index < 0) { index = vertex_to_index.size(); vertex_to_index[packed] = index; } } std::map<PackedVertex, short>::iterator iter = vertex_to_index.begin(); for (; iter != vertex_to_index.end(); iter++) { printf("%s %d\n", iter->first.str().c_str(), iter->second); } return 0; }
使用 map 容器時,須要元素能夠比較,例子中重載 <
操做符真的讓我很驚喜。這段代碼沒有使用指針,所以也不須要顯示的釋放內存,雖然添加對象到 map 容器中時,會複製 PackedVertex
對象,可是有些場合這種代碼仍是能知足需求的。code
編譯。 g++ -o test test.cpp -static 執行結果以下。對象
(4.000000,4.000000,4.000000)(0.400000,0.400000) 3 (1.000000,1.000000,1.000000)(0.100000,0.100000) 0 (3.000000,3.000000,3.000000)(0.300000,0.300000) 2 (2.000000,2.000000,2.000000)(0.200000,0.200000) 1