Use smart or raw pointer

2019-03-02 02:42发布

问题:

In the code below, there is a Grid which contains points. Likewise, Element and Face also have points but I want to them to point to points in Grid.

Should I use smart or raw pointers. If I use smart pointers should I use std::unique_ptr or std::shared_ptr?

struct Vector3
{
    vector <double> dim;

    Vector3 ()
    {
        dim.resize(3);
    }
};

struct Element
{
    vector <Vector3*> points;
};

struct Face
{
    vector <Vector3*> points;
};

struct Grid
{
    vector <Vector3> points;

    vector <Element> elms;
    vector <Face> faces;
};    

回答1:

Here you specified that the precondition is that Element and Face objects are created in a Grid object with the elements of the container referring to the same Grid containers, therefore the lifetime of all three containers (points, elms and faces) is the same.

Now you have to consider two cases.

Semi-immutable points

In this case, points is guaranteed to never invalidate references to its elements (eg. it's never modified). Here you don't need any smart pointers, you can just use a simple std::reference_wrapper as follows:

struct Vector3
{
    std::vector<double> dim;
    Vector3 () : dim(3) {}
};

template<class Type>
using ref_vec = std::vector<std::reference_wrapper<Type>>;

struct Element { ref_vec<Vector3> points; };
struct Face    { ref_vec<Vector3> points; };

struct Grid
{
    std::vector<Vector3>  points;
    std::vector<Element>  elms;
    std::vector<Face>     faces;
};

Another solution, non equivalent to your example (elms and faces don't have direct access to the Vector3 object) might be to use indexes:

struct Vector3
{
    std::vector<double> dim;
    Vector3 () : dim(3) {}
};

struct Grid
{
    struct Element { std::size_t point_indices; };
    struct Face    { std::size_t point_indices; };

    std::vector<Vector3>  points;
    std::vector<Element>  elms;
    std::vector<Face>     faces;
};

That is, you store the indices of points.

Mutable points

If the operations performed on points can invalidate references, then you might want to consider another container that does not invalidate references/pointers/iterators to the element.

For example std::deque guarantees the validity of references for deletion/insertion at the beginning and end of the container.

Once you have chosen the correct container you can just apply the same ideas as above.