Which kind of pointer do I use when?

后端 未结 4 1070
遥遥无期
遥遥无期 2020-11-22 03:39

Ok, so the last time I wrote C++ for a living, std::auto_ptr was all the std lib had available, and boost::shared_ptr was all the rage. I never rea

4条回答
  •  迷失自我
    2020-11-22 04:10

    Deciding what smart pointer to use is a question of ownership. When it comes to resource management, object A owns object B if it is in control of the lifetime of object B. For example, member variables are owned by their respective objects because the lifetime of member variables is tied to the lifetime of the object. You choose smart pointers based on how the object is owned.

    Note that ownership in a software system is separate from ownership as we would think of it outside of software. For example, a person might "own" their home, but that doesn't necessarily mean that a Person object has control over the lifetime of a House object. Conflating these real world concepts with software concepts is a sure-fire way to program yourself into a hole.


    If you have sole ownership of the object, use std::unique_ptr.

    If you have shared ownership of the object...
    - If there are no cycles in ownership, use std::shared_ptr.
    - If there are cycles, define a "direction" and use std::shared_ptr in one direction and std::weak_ptr in the other.

    If the object owns you, but there is potential of having no owner, use normal pointers T* (e.g. parent pointers).

    If the object owns you (or otherwise has guaranteed existence), use references T&.


    Caveat: Be aware of the costs of smart pointers. In memory or performance limited environments, it could be beneficial to just use normal pointers with a more manual scheme for managing memory.

    The costs:

    • If you have a custom deleter (e.g. you use allocation pools) then this will incur overhead per pointer that may be easily avoided by manual deletion.
    • std::shared_ptr has the overhead of a reference count increment on copy, plus a decrement on destruction followed by a 0-count check with deletion of the held object. Depending on the implementation, this can bloat your code and cause performance issues.
    • Compile time. As with all templates, smart pointers contribute negatively to compile times.

    Examples:

    struct BinaryTree
    {
        Tree* m_parent;
        std::unique_ptr m_children[2]; // or use std::array...
    };
    

    A binary tree does not own its parent, but the existence of a tree implies the existence of its parent (or nullptr for root), so that uses a normal pointer. A binary tree (with value semantics) has sole ownership of its children, so those are std::unique_ptr.

    struct ListNode
    {
        std::shared_ptr m_next;
        std::weak_ptr m_prev;
    };
    

    Here, the list node owns its next and previous lists, so we define a direction and use shared_ptr for next and weak_ptr for prev to break the cycle.

提交回复
热议问题