递归复制构造函数?

时间:2011-03-16 23:43:54

标签: c++ constructor

我正在尝试编写一个复制构造函数,它接受四叉树的节点,并复制该节点及其所有子节点及其子节点,依此类推。这就是我所拥有的:

 Node(const Node & n) {

    nChild=new Node(*(n.nwChild));
    neChild=new Node(*(n.nChild));
    eChild=new Node(*(n.seChild));
    wChild=new Node(*(n.sChild));   
}

我觉得最后4行有点时髦。我正在做的事情有意义吗?

5 个答案:

答案 0 :(得分:2)

对我有意义。这是一个递归的深层副本,应该可以正常工作。但是不要忘记在析构函数中释放所有这些内存。并且在编码时要注意Quadtree的临时副本,如果树很大,它们可能是残酷的。

此代码唯一真正的问题是距离异常安全还有几英里远。首先,它应该使用initializer list编写,这样:

Quadtree::QuadtreeNode::QuadtreeNode(const QuadtreeNode & n) 
 : x(n.x), y(n.y), height(n.height), width(n.width),
   nwChild(new QuadtreeNode(*(n.nwChild)), // ...

构造函数体可以保持为空,除非您要做的不仅仅是初始化成员。执行此操作后,您可能需要考虑交换某些智能指针的裸指针。这将是使类异常安全的良好基础。

答案 1 :(得分:2)

您的构造函数不是异常安全的 - 假设第二个new抛出bad_alloc,那么第一个将如何清理?

通过将Child字段更改为您喜欢的智能指针,您可以让自己的生活更轻松。如果你没有最喜欢的智能指针,请向自己介绍一些; - )

[编辑:刚刚注意到你没有提供数据成员定义,我只是假设它们是原始指针。抱歉,如果他们已经是聪明的指针。]

早期看起来有点奇怪 - 如果rhs上的所有四个子节点都是NULL,那么x / y /高度/宽度是不是被复制了?通常,这些成员将由初始化列表处理。如果任何的子项为NULL,而不是所有,那么你应该做些什么吗?目前,您不处理在一个方向上具有子节点但不是全部4的节点。如果它是树的属性,每个节点有4个子节点或者没有节点,那么您不需要检查所有4个,但是在复制叶节点时,需要初始化各个字段。如果一个节点可能只有一个子节点,那么就不能正确复制该节点。

除此之外,基本逻辑对我来说似乎没问题,在没有出错的意义上,它会以递归方式复制。

答案 2 :(得分:2)

除了这里的好答案(我赞成Jon的),我想介绍一种很酷的新方法来获得C ++ 0x中的异常安全性。你的编译器可能还没有实现它。但是从现在开始,我一年前都寄予厚望。但事实并非如此。

我从Jon的复制构造函数开始:

Quadtree::QuadtreeNode::QuadtreeNode(const QuadtreeNode & n) 
 : x(n.x), y(n.y), height(n.height), width(n.width),
   nwChild(new QuadtreeNode(*(n.nwChild)), // ...

现在假设您有一个默认构造函数,如您在其中一条评论中所描述的那样:

Quadtree::QuadtreeNode::QuadtreeNode() 
 : x(0), y(0), height(0), width(0),
   nwChild(nullptr), // ...

现在,您可以通过首先调用默认构造函数将Jon的复制构造函数转换为异常安全的构造函数! :-)而且具有讽刺意味的是,它看起来很像你的原始代码:

Quadtree::QuadtreeNode::QuadtreeNode(const QuadtreeNode & n)
     : QuadtreeNode()
{
    if(!(n.nwChild==NULL && n.neChild==NULL && n.seChild==NULL && n.swChild==NULL))
    {
        x=n.x;
        y=n.y;
        height=n.height;
        width=n.width;
        nwChild=new QuadtreeNode(*(n.nwChild));
        neChild=new QuadtreeNode(*(n.neChild));
        seChild=new QuadtreeNode(*(n.seChild));
        swChild=new QuadtreeNode(*(n.swChild)); 
    }
}

说明:这称为委托构造函数。并且是C ++ 0x的新功能。你可以从另一个字面上调用一个构造函数。当任何构造函数完成时,该对象被视为构造。从那时起,任何抛出的异常都将激活析构函数进行清理。因此,默认构造函数通常是以 noexcept 方式构造对象的便捷方式。然后,您可以以自然的方式构建对象,因为知道如果抛出异常将调用析构函数。

答案 3 :(得分:1)

如何分配节点?如果遇到这种情况我会看到一个问题

 1   3
   5 
 7   9

如果我要复制5,而1有一个指向se的{​​{1}}指针,则会重新制作5,而5会重新制作1 {1}}等等。

答案 4 :(得分:0)

首先,我在理解反应时遇到一些困难, 因为它们指的是我没有看到的条件测试 你的原始代码。然而...

IIUC,你想做一个深层复制。有两个问题 必须考虑:如果一个(或多个)的话该怎么办 指针为null,以及如何使代码异常安全。该 对这两者最明显的解决方案是使用智能指针 对于每个指针,它执行深层复制并生成所有指针 必要的检查。设计一个好的通用copy_ptr不是 微不足道,因为它也应该处理多态指针。 但对于任何一个案例,或给定的约定,它都很简单; 如果Node不是多态的,那么shared_ptr的copy ctor就可以了 就像这样简单:

copy_ptr( T const& other )
    : m_ptr( other.m_ptr == NULL ? NULL : new T( *other.m_ptr ) )
{
}
~copy_ptr()
{
    delete m_ptr;
}

(使用交换习语作为对齐,并提供其他通常的 智能指针功能。)

执行此操作,您可以将初始化程序直接放入 初始化列表:

Node( Node const& other )
    : nChild( other.nChild )
    , neChild( other.neChild )
    //  ...

(没有智能指针,或者至少是一个单独的类类型 封装每个指针,你必须初始化 在初始化列表中指向null,然后分配 在try-catch块中新建指针以便删除 在发生异常时已经新建的对象。)