如何制作shared_ptr的深度副本?

How to make deep a copy of shared_ptr?

提问人:user11225404 提问时间:3/15/2023 最后编辑:Benjamin Buchuser11225404 更新时间:3/16/2023 访问量:269

问:

我有一个简单的节点树类。它是 的子类。 并且是对象。类是一个抽象类。 执行乘法的导数函数。MultNodeNode_pRight_pLeftstd::shared_ptr<Node>NodeMultNode::DerFun()Node

\frac{d}{dx}[f(x)g(x)]=f(x)g'(x)+f'(x)g(x)

我必须创建临时的左右,我只需要使用智能指针。我不知道如何创建 的深层副本。Node(pTmpR and pTmpL)shrared_ptr

void MultNode::DerFun() {
    auto pTmpR =  _pRight;
    auto pTmpL = _pLeft;
    _pRight->DerFun(); // sin(pi/4) * cos(pi/4);
    _pLeft->DerFun();  // der(sin(pi/4) * cos(pi/4));
    _pRight = std::make_shared<AddNode>(
        std::make_shared<MultNode>(_pRight, pTmpL),
        std::make_shared<MultNode>(pTmpR, _pLeft));
    _pLeft = std::make_shared<NumNode>(1);
}
C++ 提升 STL 智能指针

评论

0赞 463035818_is_not_an_ai 3/15/2023
*ptr是由指针管理的对象
0赞 463035818_is_not_an_ai 3/15/2023
当它是临时的时,没有必要将其共享
5赞 joergbrech 3/15/2023
如果我正确理解你的问题,你似乎正在构建一个表达式树,其中表示乘法。 似乎是 的虚拟方法,它用其导数替换表达式树中的节点,从而改变原始节点。从概念上讲,a 的导数是 ,但您只需将 的一个操作数替换为 an 并将另一个操作数设置为 1。虽然这可能有效,但我认为这是一个重大的设计缺陷。Wy 不创建一个方法并让它返回正确类型的新节点吗?MultNodeDerFunvoidNodeMultNodeAddNodeMultNodeAddNodeDerFunconst
1赞 molbdnilo 3/15/2023
我认为你想得太多了 - 如果你想要一个 拥有的对象的副本,并且你希望共享副本:pstd::make_shared<type>(*p);
4赞 molbdnilo 3/15/2023
如果你沿着这条路走下去,你会发现自己写了很多可怕和混乱的代码来清理 s 和 s。最好让差异化创建一个新树,而不是破坏性地更新它。* 1+ 0

答:

4赞 Ahmed AEK 3/15/2023 #1

编译器无法通过指针进行深度复制,因为它不知道在编译时要复制什么。DerivedBase

解决方案是创建一个虚拟函数,该函数负责制作此派生对象的深拷贝,该函数将调用其所有子级来构造真正的深拷贝,并且由于它是在指针上调用的,因此将在运行时调用正确的函数。deepcopydeepcopy

#include <memory>

class Node {
public:
    virtual std::shared_ptr<Node> deepcopy() const = 0;
    virtual ~Node() = default;
};

class MultNode: public Node {
private:
    std::shared_ptr<Node> m_pleft;
    std::shared_ptr<Node> m_pright;
public:
    virtual std::shared_ptr<Node> deepcopy() const
    {
        return std::make_shared<MultNode>(m_pleft->deepcopy(), m_pright->deepcopy());
    }
    MultNode(std::shared_ptr<Node> pleft, std::shared_ptr<Node> pright) : 
        m_pleft(pleft), m_pright(pright) {};
};
3赞 n. m. could be an AI 3/15/2023 #2

根本没有必要深度复制任何内容。你有共享的指针,利用它们来发挥你的优势。

这是一个可以派生的最小系统。请注意,它生成的是 DAG,而不是树。一切都是,没有什么是永远不会改变的。节点尽可能共享。const

#include <memory>

class Node;

using NodePtr = std::shared_ptr<const Node>;

using Number = double;

class Node
{
    public:
        virtual ~Node() = default;
        virtual NodePtr derive() const = 0;
};

class AddNode : public Node
{
    public:
        AddNode (const NodePtr& a, const NodePtr& b) : a(a), b(b) {}
    private:
        NodePtr a, b;
        NodePtr derive() const override 
        { 
            return std::make_shared<AddNode>(a->derive(), b->derive()); 
        }
};

class MultNode : public Node
{
    public:
        MultNode (const NodePtr& a, const NodePtr& b) : a(a), b(b) {}
    private:
        NodePtr a, b;
        NodePtr derive() const override
        {
            const auto l = std::make_shared<MultNode>(a, b->derive());
            const auto r = std::make_shared<MultNode>(a->derive(), b);
            return std::make_shared<AddNode>(l, r);
        }
};

class ConstNode : public Node
{
    public:
        ConstNode (Number v) : v(v) {}
    private:
        Number v;
        NodePtr derive() const override 
        { 
            return std::make_shared<ConstNode>(Number{0}); 
        }
};

class VarNode : public Node
{
    public:
        VarNode() {}
    private:
         NodePtr derive() const override 
         { 
             return std::make_shared<ConstNode>(Number{1}); 
         }
};

警告:

  1. 没有经过太多测试
  2. 仅支持一个变量(易于更改)
  3. 缺少许多有用的节点,例如除法、函数组成、幂......(易于添加)
  4. 没有常量传播或其他简化表达式的方法(可以作为单独的方法添加)

评论

1赞 Caleth 3/15/2023
你甚至可以定义等NodePtr operator*(NodePtr l, NodePtr r) { return std::make_shared<MultNode>(l, r); }
0赞 sehe 3/15/2023
是的。这就是东西。Sean Parent® 批准了
0赞 sehe 3/15/2023
我只记得我过去也做过类似的评论,不举个例子就觉得很糟糕,所以这里是:stackoverflow.com/questions/27708139/transforming-trees-in-c/......