shared_ptr,unique_ptr,ownership,在这种特殊情况下我是否过度了?

use*_*490 7 c++ shared-ptr c++11 c++14

我从事图形应用程序并且一直在使用共享和唯一指针,因为它为我处理内存释放(也称为便利),这可能很糟糕(如果这就是我使用它们的原因).

我最近在Stackoverflow上阅读了一个问题的答案,提到根据B. Stroustrup,通常不应该使用唯一/共享ptrs,而且应该通过值传递参数.

我有一个图形的情况,我认为使用shared_ptr是有道理的,但我想知道专家(我不是C++专家),如果我过度做/思考它,如果是这样,他们会做什么而不是(为了符合C++建议和效率)

我将解决渲染/光线跟踪中出现的一般问题.在这个特殊的问题中,我们有一个对象池(我们将使用三角形进行此解释)和一个结构,为了简化说明,我们将其称为常规3D网格.让我们说在某些时候我们需要将三角形插入到网格中:这意味着我们需要检查每个插入三角形的边界体积是否与网格中的任何单元格重叠,然后是每个重叠的单元格需要保持指向该三角形的指针/引用(供以后使用).一个三角形可能会重叠超过1个单元格,因此它可以被多个单元格引用多次(您可以看到我在shared_ptr这里的位置).

请注意,在网格结构之外,我们不需要三角形池(因此从技术上讲,拥有三角形池的对象是网格,或者更确切地说是网格的单元格).

class Grid
{
    struct Cell
    {
        std::vector<std::shared_ptr<const Triangle>> triList; 
    };
    void insert(triangle*& tri_)
    {
        std::shared_ptr<const Triangle> tri = tri_;
        for (each cell overlapped by tri) {
            // compute cell index
            uint32_t i = ...
            cells[i].triList.push_back(tri);
        }
    }
    Cell cells[RES * RES * RES];
};

void createPoolOfTrianglesAndInsertIntoGrid()
{
    Grid grid;
    uint32_t maxTris = 32;
    Triangle* tris = new Triangles[maxTris];
    // process the triangles
    ...
    // now insert into grid
    for (uint32_t i = 0; i < maxTris; ++i) {
        // use placement new
        Triangle* tri = new (&tris[i]) Triangle;
        grid.insert(tri);
    }
    // no need to delete tris here ... it should be done by 
    // the grid when we go out of this function's scope
}
Run Code Online (Sandbox Code Playgroud)

这听起来很复杂,但我的设计背后的动机是遵循Stroustrup的建议.在这种情况下,函数createPoolOfTrianglesAndInsertIntoGrid不拥有三角形,它是网格的单元格.所以我在函数中分配内存,createPoolOfTrianglesAndInsertIntoGrid因为这是我需要创建三角形的地方,然后我使用placement new方法获取指向该池中每个三角形的指针,然后我可以将其传递给网格insert方法(我推迟内存)管理该对象到该方法).在那里,它将三角形转换为a,shared_ptr并且单元格现在可以共享它的"引用"(使用shared_ptr).

我想知道你是否认为这是正确的方向,或者如果这看起来完全错误,无论是在实施方面还是在可能的效率损失方面(我分配了一个内存池,然后使用新的位置来创建一个临时三角形,然后我将其传递给网格插入方法,然后转换为shared_ptr,...)

我正在努力学习和改进我的代码,并希望您能提供有价值的专业反馈.

编辑:基本上我正在尝试找到解决该问题的正确方法+我将尝试根据您的评论提供稍后的一些修改

Gal*_*lik 4

在我看来,Grid三角形好像是你的。我假设三角形相对较轻(3-5 维?)。

我认为这样的事情可能适合。我正在使用中的容器按值Grid获取三角形的所有权。当超出范围时,容器将删除三角形。Grid

然后每个都Cell简单地使用原始指针来跟踪它引用的三角形。sCell不拥有三角形,它们只是保存指向 所拥有的三角形的指针Grid

class Grid
{
    struct Cell
    {
        std::vector<Triangle*> triList; // non owning
    };

    void insert(Triangle tri) // pass by value
    {
        tris.push_back(tri); // Grid owns this by value

        for(each cell overlapped by tri) {
            // compute cell index
            uint32_t i = ...

            cells[i].triList.push_back(&tris.back());
        }
    }

    // Use a deque because it won't re-allocate when adding
    // new elements to the end
    std::deque<Triangle> tris; // elements owned by value

   Cell cells[RES * RES * RES]; // point to owned elements
};

void createPoolOfTrianglesAndInsertIntoGrid()
{
    Grid grid; // owns the triangles (by value)

    uint32_t maxTris = 32;

    std::vector<Triangle> tris(maxTris);
    // process the triangles
    // ...
    // now insert into grid
    for(auto tri: tris)
        grid.insert(tri);
    }

    // no need to delete tris here ... it should be done by
    // the grid when we go out of this function's scope
}
Run Code Online (Sandbox Code Playgroud)

注意:我使用 astd::deque按值存储三角形Grid。这是因为在添加新三角形时它不会重新分配其内部内存。如果您在这里使用 a ,那么std::vector当调整自身大小时,您的原始指针最终会悬空std::vector

或者:

鉴于您似乎在函数中构建了所有三角形,然后将它们全部传递给Grid,为什么要一次一个呢?你可以一口气把整个容器放进去。如果您使用移动语义来执行此操作,您甚至不必复制任何内容:

class Grid
{
    struct Cell
    {
        std::vector<Triangle*> triList; // non owning
    };

    // Accept the entire container in-tack
    // (no reallocations allowed after this point)
    void insert(std::vector<Triangle> tris) // pass by value (able to move in)
    {
        //
        for(auto& tri: tris)
        {
            for(each cell overlapped by tri) {
                // compute cell index
                uint32_t i = ...

                cells[i].triList.push_back(&tri);
            }
        }
    }

    // Using a vector so it MUST NOT be resized after
    // Cells have been pointed to its elements!!!
    std::vector<Triangle> tris; // elements owned by value
    Cell cells[RES * RES * RES]; // point to owned elements
};

void createPoolOfTrianglesAndInsertIntoGrid()
{
    Grid grid; // owns the triangles (by value)

    uint32_t maxTris = 32;

    // Build the triangles into
    std::vector<Triangle> tris(maxTris);
    // process the triangles
    // ...
    // now insert into grid
    grid.insert(std::move(tris)); // move the whole darn container (very efficient)

    // no need to delete tris here ... it should be done by
    // the grid when we go out of this function's scope
}
Run Code Online (Sandbox Code Playgroud)

注意:现在我使用了 a,std::vector因为在三角形到达 后,您不会将它们一一添加Grid。但是您必须确保拥有的`std::vector内部的 Grid大小没有调整。