如何从一个const对象中深度复制一个map

7

我有一个问题一直无法解决,也没有在这个网站上找到相关解答...

我有一个称为DataObject的对象,其中包含一个被声明如下的映射:

std::map<size_t, DataElement*> dataElements;

现在我有一个复制函数(用于复制构造函数):
void DataObject::copy(DataObject const &other) {

    //here some code to clean up the old data in this object...

    //copy all the elements:
    size = other.getSize();
    for(size_t i = 0; i < size; ++i) {
            DataElement* dat = new DataElement(*other.dataElements[i]);
            dataElements[i] = dat;
    }

}

这段代码无法编译,因为在一个const对象上不能使用dataElements[i]。那么如何对被const对象所拥有的map中的所有元素进行深拷贝?

我知道在const map上可以使用find()函数,但是如何获取我想要拷贝的实际对象呢?


什么是错误?什么是DataElement?它的构造函数需要哪些参数? - Kiril Kirov
1
你的函数没有标记为const,那么dataElements怎么是const的呢? - Puppy
1
如果你的键从0到N,那么很可能map不是你尝试做的事情的正确集合。为什么不只使用vector或deque呢? - CashCow
@DeadMG:我的函数没有标记为const,但是复制构造函数必须采用一个const对象,并且您不能修改const对象的成员。 - openbas2
@CashCow:你说得对,我应该使用向量,但我遇到了这个问题,想知道如何解决它。 - openbas2
显示剩余3条评论
6个回答

10
std::map<size_t, DataElement*>::const_iterator it = other.dataElements.begin();
while(it != other.dataElements.end())
{
    dataElements[it->first] = new DataElement(*(it->second));
    ++it;
}

我相信这个应该可以工作。

它会工作,但如果您的映射已经在该位置具有元素,则会覆盖它,从而丢失您需要删除的指针(并且会出现泄漏)。您应该首先检查这一点。 - CashCow
1
如果“…清理旧数据的某些代码…”将this->dataElements清空,这段代码是可以的。 - Useless
如果 dataElements[it->first] 存在的话,需要删除它。可以这样实现:void replace(DataElement*& old, DataElement* newv) { delete old; old=newv } 然后调用 replace(dataElements[it->first],new DataElement(*(it->second)))。 - CashCow
@CashCow-- 正如Useless所说,根据OP的评论,“//这里是一些代码来清理该对象中的旧数据……”旧数据已经被清除。 - Benjamin Lindley
@PigBen 所以我们假设我们要写入的地图是空的,在这种情况下它将正常工作。 - CashCow
我确实首先清理了地图(并删除了相关对象)。这非常有效! - openbas2

1
你需要使用std::transform。它在进行复制的同时,还对每个元素执行函数操作。在你的情况下,这将实现对值进行深拷贝。
因此,下面的代码可以作为一个转换器:
class DeepCopyMapPointer
{
   typedef std::map<size_t, DataElement*> map_type;
   typedef map_type::value_type value_type;

public:
   value_type operator()( const value_type & other ) const
   {
      return value_type(other.first, new DataElement(*other.second) );
   }
};

void DataObject::copy(DataObject const &other) 
{
   std::transform(other.dataElements.begin(), other.dataElements.end(),
      std::inserter( dataElements, dataElements.end() ), DeepCopyMapPointer() );
}

这并不是那么简单,因为如果您复制一个元素并且由于此操作导致插入失败,则会出现泄漏。您可以通过编写自己的插入器而不是使用std::inserter来解决这个问题... 这有点棘手,但这是您下一个练习。


1

由于您的映射只有从0n-1的整数键,因此只需将容器类型更改为向量,您当前的代码应该可以很好地工作(您需要调整目标容器的大小以确保有足够的空间可用)。

如果出于某种原因需要使用map(现有API?),则如您所发现的那样,operator[]仅具有非const版本。

而是使用const_iterator方法(从@PigBen的答案中获得了赞同和采纳):

std::map<size_t, DataElement*>::const_iterator it = other.dataElements.begin();
while(it != other.dataElements.end())
{
    dataElements[it->first] = new DataElement(*(it->second));
    ++it;
}

0

现在没有太多时间回答,所以这里会很简短。map有一个复制构造函数,但它不会进行深度复制。你需要使用迭代器(map.begin(), map.end())。*Iter将给你一个pair对象,所以你可以做(*iter).first和/或(*iter).second。(或者类似的东西...已经过了一段时间...)

参考: http://www.sgi.com/tech/stl/Map.html


0
for (auto& kv : other.dataElements) {
            dataElements[kv.first] = new DataElement(*kv.second);
        }

-1
只有一个观察:您正在直接访问dataElements(other.dataElements)。将dataElements保持为私有,然后提供像GetDataElement这样的方法。

不,它是同一个类。它可能是私有的,但同一类的另一个实例将具有访问权限,因为私有不是“每个对象”的访问限定符。 - CashCow
1
不要这样做。Get/Set方法是封装的对立面。还要注意,一个类总是它自己的朋友。因此,它可以轻松地访问其他对象的元素。这并不会破坏封装,因为对象应该知道彼此(因为它们是相同类型的,它们怎么可能不知道彼此的实现细节)。 - Martin York
我确实一直学习到,在复制构造函数中这样做是可以的(或者像这样使用的函数)。DataObject和DataElement的成员当然是私有的。 - openbas2

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