std::map的浅拷贝和深拷贝

3
如何最好地实现这些?我考虑了类似下面这样的方案:
    using namespace std;

    shape_container
    shape_container::clone_deep () const
    {
        shape_container* ptr = new shape_container();
        copy( data.begin(), data.end(), (*ptr).begin() );
        return *ptr;
    }

    shape_container
    shape_container::clone_shallow () const
    {
        return *( new shape_container(*this) );
    }

成员变量 data 的定义如下:
    std::map<std::string, shape*> data;

很遗憾,这个不起作用。以下是编译器错误,我真的不太理解它们:

    g++ -Wall -O2 -pedantic -I../../UnitTest++/src/ -I./libfglwin/include/ -I. -c shape_container.cpp -o shape_container.o
    /usr/include/c++/4.2.1/bits/stl_pair.h: In member function ‘std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*>& std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*>::operator=(const std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*>&)’:
    /usr/include/c++/4.2.1/bits/stl_pair.h:69:   instantiated from ‘static _OI std::__copy<<anonymous>, <template-parameter-1-2> >::copy(_II, _II, _OI) [with _II = std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, _OI = std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, bool <anonymous> = false, <template-parameter-1-2> = std::bidirectional_iterator_tag]’
    /usr/include/c++/4.2.1/bits/stl_algobase.h:315:   instantiated from ‘_OI std::__copy_aux(_II, _II, _OI) [with _II = std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, _OI = std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >]’
    /usr/include/c++/4.2.1/bits/stl_algobase.h:340:   instantiated from ‘static _OI std::__copy_normal<<anonymous>, <anonymous> >::__copy_n(_II, _II, _OI) [with _II = std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, _OI = std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, bool <anonymous> = false, bool <anonymous> = false]’
    /usr/include/c++/4.2.1/bits/stl_algobase.h:401:   instantiated from ‘_OutputIterator std::copy(_InputIterator, _InputIterator, _OutputIterator) [with _InputIterator = std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, _OutputIterator = std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >]’
    shape_container.cpp:70:   instantiated from here
    /usr/include/c++/4.2.1/bits/stl_pair.h:69: error: non-static const member ‘const std::basic_string<char, std::char_traits<char>, std::allocator<char> > std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*>::first’, can't use default assignment operator
    /usr/include/c++/4.2.1/bits/stl_algobase.h: In static member function ‘static _OI std::__copy<<anonymous>, <template-parameter-1-2> >::copy(_II, _II, _OI) [with _II = std::_Rb_tree_const_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, _OI = std::_Rb_tree_iterator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*> >, bool <anonymous> = false, <template-parameter-1-2> = std::bidirectional_iterator_tag]’:
    /usr/include/c++/4.2.1/bits/stl_algobase.h:268: note: synthesized method ‘std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*>& std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*>::operator=(const std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, shape*>&)’ first required here 

这看起来对我来说有点过于复杂了,是这样吗?我能不能改进一下呢?

顺便说一句,我在从shape派生的类中有clone()方法。也许我可以将它们用于clone_deep方法?它们可以吗?它们看起来像这样:

    class shape
    {
        public:
            /* Many methods. */
            virtual shape* clone () const = 0;

        protected:
            colorRGB    color_;
            std::string name_;
    };

    class triangle2d : public shape
    {
        public:
            /* Many methods. */
            triangle2d* clone() const;
        private:
            point3d a_, b_, c_;
    };

    triangle2d*
    triangle2d::clone() const
    {
        return new triangle2d(*this);
    } 

你想要浅拷贝/深拷贝地图,还是地图所持有的形状? - Adam Bowen
谁或什么负责删除存储在映射中的内容?深/浅拷贝的整个概念在C++中有些反模式。 - anon
浅拷贝 shape_container 应该返回一个复制的 map,其中包含指向 相同 形状的指针。深拷贝应该返回一个复制的 map,其中包含指向 复制的 形状的指针。 - Patrick Oscity
这不是一个反模式,而是没有意义。每个类定义了它可以被复制的一种方式。类的拷贝构造函数和赋值运算符定义了类成员是否被复制。这不像Java中的x = y执行的是浅拷贝(引用拷贝)。在C++中,它调用赋值运算符,将对象本身进行复制。(除非xy是指针) - jalf
4个回答

1
通常,克隆函数会返回指向新实例的指针。而你返回的是一个按值传递的对象,它是从动态分配的实例复制构造而来的,然后泄漏了。
如果你想按值返回,则不应使用 new。 例如:
shape_container shape_container::clone_shallow () const
{
    return *this;
}

如果data成员只是一个std::map实例,那么它将作为浅克隆的一部分被复制,因此在深度克隆情况下没有必要执行std::copy,它不会尝试做任何不同的事情。
如果您想要对映射进行std::copy,则需要使用std::insert_iterator
我认为在事后对每个形状进行clone可能更容易。
例如:
shape_container shape_container::clone_deep() const
{
    shape_container ret(*this);

    for (std::map<std::string, shape*>::iterator i = ret.data.begin(); i != ret.data.end(); ++i)
    {
        i->second = i->second->clone();
    }

    return ret;
}

对于通常的浅拷贝函数,我只需要执行 return this;。我猜这会在堆上创建一个副本?我想让 clone_shallow 明确地仅复制容器。deep_copy 还应该复制地图指向的元素。 - Patrick Oscity
顺便说一句,我不一定需要返回值。我只是认为这是一个好主意,但可能并不是。但我愿意学习 :) - Patrick Oscity
我更喜欢使用按值返回,因为它可以明确地传递所有权。对于您在映射中的“shape”对象,这是一个问题,因为浅克隆会给您带来隐式共享所有权,而深克隆必须拥有新形状的所有权。您如何管理形状的生命周期? - CB Bailey
非常感谢您的回答/评论!我还没有管理好生命周期,但我计划实现引用计数来管理我的形状类。否则,可能会删除仍被其他容器引用的元素。这是您所说的吗? - Patrick Oscity
1
是的,没错。如果你需要共享所有权,你应该考虑使用shared_ptr而不是裸指针。如果可用的话,可以选择std::tr1::shared_ptr或者boost::shared_ptr。这样你就不必在对象本身中实现引用计数了。 - CB Bailey
我想我会实现引用计数,以便看看它的工作原理,因为整个项目更像是一个虚拟项目,让我学习C++和一些基本概念和模式。之后,我会看看共享指针。不过还是谢谢你的提示! - Patrick Oscity

0
一个选择是将您的形状类型包装在一个执行对象深拷贝的类型中:
class shape_deep_copy_wrapper {
  // ...
public:
  shape_deep_copy_wrapper (shape * shape)
  : m_my_shape (shape)
  {
  }

  shape_deep_copy_wrapper (shape_deep_copy_wrapper const & rhs)
  : m_my_shape (rhs.m_my_shape.deep_copy ())
  {
  }

  // ...

private:
  shape * m_my_shape;
};

然后用这种类型构建一个映射(map):

typedef std :: map < shape_deep_copy_wrapper , ... > DeepCopy ;
typedef std :: map < shape* , ... >                  ShallowCopy ;

0
首先,您的示例泄漏内存,因为您在方法中使用了new一个shape_container,但是它通过返回值被复制出去了。您应该像shape示例一样返回指针。
编译器错误似乎与复制有关,因为它抱怨无法为您生成赋值运算符。同样,尝试使用指针,这个问题就会消失。

0

如果你要进行地图的深拷贝,那么你必须创建一个新的地图,并将所有元素进行深拷贝。

考虑使用引用计数方法,这将是更好的方法。


我稍后会做引用计数,但首先我想让复制工作 :) 不过你说得对。但无论如何,我想要有深度克隆整个东西的可能性。 - Patrick Oscity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接