2

虽然已经有很多关于 SO 上的复制构造函数/赋值运算符的问题,但我没有找到适合我的问题的答案。

我有一个像

class Foo
{
   // ...
private:
   std::vector<int> vec1;
   std::vector<int> vec2;
   boost::bimap<unsigned int, unsigned int> bimap;
   // And a couple more
};

现在似乎有一些相当多的复制正在进行(基于配置文件数据)。所以我的问题是如何最好地解决这个问题?

我应该实现自定义复制构造函数/赋值运算符并使用交换吗?或者我应该定义自己的交换方法并使用它(在适当的情况下)而不是赋值?

由于我不是 C++ 专家,因此非常感谢展示如何正确处理这种情况的示例。

更新:看来我不是很清楚..让我试着解释一下。该程序基本上是一个即时广度优先搜索程序,对于所采取的每一步,我都需要存储有关该步骤(即Foo类)的元数据。现在的问题是(通常)有指数级的步骤,所以你可以想象需要存储大量这些对象。据我所知,我总是通过(const)引用。每次我从图中的节点计算后继者时,我都需要创建和存储一个 Foo 对象(但是,在处理这个后继对象时,一些数据成员将被添加到这个 foo 中)..

我的个人资料数据大致如下所示(我没有这台机器上的实际数字):

SearchStrategy::Search    13s
FooStore::Save            10s

所以你可以看到我在保存这些元数据上花费的时间几乎和我在图表中搜索所花费的时间一样多。哦,FooStore 保存Foo在一个google::sparse_hash_map<long long, Foo, boost::hash<long long> >.

编译器是 g++4.4 或 g++4.5(我不在我的开发机器上,所以我现在无法检查)..

更新 2我在构造后将一些成员分配给 Foo 实例,例如

void SetVec1(const std::vector<int>& vec1) { this->vec1 = vec1; };

我想明天,我应该把它改成使用交换方法,这肯定会改进一点..

如果我不完全清楚我想要实现什么语义,我很抱歉,但原因是我不太确定。

问候,

莫腾

4

6 回答 6

3

Everything depends on what copying this object means in your case :

  1. it means copying it's whole value
  2. it means the copied object will refer to the same content

If it's 1, then this class seem correct. You're not very clear about the operations that you say does make lot of copies so I'm assuming you try to copy the whole object.

If it's 2, then you need to use something like shared_ptr to share the containers between the objects. Just using shared_ptr instead of real objects as member will implicitely allow the buffers to be refered by both objects (the copy and the copied). That's the easier way (using boost::shared_ptr or std::shared_ptr if you have a C++0x enabled compiler providing it).

There are harder ways but they will certainly become a problem later.

于 2011-05-10T19:00:38.163 回答
2
  1. 当然,大家都这么说,不要过早优化。除非你证明 a) 你的程序运行得太慢,并且 b) 如果你不复制这么多数据,它会运行得更快。

  2. 如果您的程序设计要求您同时保存多个数据副本,那么您无能为力。你只需要硬着头皮复制数据。不,实现自定义复制构造函数和自定义赋值运算符不会让它更快。

  3. 如果您的程序不需要此数据的多个同时副本,那么您确实有一些技巧可以减少您执行的副本数量。

检测你的复制方法如果是我,我会做的第一件事,甚至在尝试改进任何东西之前,就是计算我的复制方法被调用的次数。

class Foo {
private:
  static int numberOfConstructors;
  static int numberofCopyConstructors;
  static int numberofAssignments;
  Foo() { ++numberOfConstructors; ...; }
  Foo(const Foo& f) : vec1(f.vec1), vec2(f.vec2), bimap(f.bimap) {
    ++numberOfCopyConstructors;
    ...;
  }
  Foo& operator=(const Foo& f) {
    ++numberOfAssignments;
    ...;
  }
};

在有或没有改进的情况下运行您的程序。打印出这些静态成员的值以查看您的更改是否有任何效果。

避免在函数调用中使用引用进行赋值如果将 Foo 类型的对象传递给函数,请考虑是否可以通过引用来完成。如果您不更改传递的副本,则通过 const 引用传递它是不费吹灰之力的。

// WAS:
extern SomeFuncton(Foo f);
// EASY change -- if this compiles, you know that it is correct
extern SomeFunction(const Foo& f);
// HARD change -- you have to examine your code to see if this is safe
extern SomeFunction(Foo& f);

使用 Foo::swap 避免复制如果您经常使用复制方法(显式或隐式),请考虑分配的项目是否可以放弃其数据,而不是复制它。

// Was:
vectorOfFoo.push_back(myFoo);
// maybe faster:
vectorOfFoo.push_back(Foo());
vectorOfFoo.back().swap(myFoo);

// Was:
newFoo = oldFoo;
// maybe faster
newfoo.swap(oldFoo);

当然,这仅在myFoo并且oldFoo不再需要访问他们的数据时才有效。而且,你必须实施Foo::swap

void Foo::swap(Foo& old) {
    std::swap(this->vec1, old.vec1);
    std::swap(this->vec2, old.vec2);
    ...
}

不管你做什么,在你改变之前和之后衡量你的计划。测量你的复制方法被调用的次数,以及你的程序的总时间改进。

于 2011-05-10T19:19:16.790 回答
1

Your class doesn't seem that bad, but you do not show how you use it.

If there is lots of copying, then you need to pass objects of those class by reference (or if possible const reference). If that class has to be copied, then you can not do anything.

于 2011-05-10T18:33:10.597 回答
1

Copying of huge vectors unlikely can be cheap. The most promising way is to copy rarer. While it's quite easy (may be too easy) in C++ to invoke copy without intention, there are ways to avoid needless copying:

  • passing by const and non-const reference
  • move-constructors
  • smart pointers with ownership transfer

These techniques may leave only copies which are required by algorithm.

Sometimes it's possible to avoid even some of those copying. For example, if you need two objects where the second one is reversed copy of the first one, a wrapper object may be created which acts like reversed, but instead of storing entire copy has only a reference.

于 2011-05-10T19:00:29.120 回答
1

如果确实有问题,您可以考虑实施pimpl idiom。但我怀疑这是一个问题,尽管我必须看到你对这个类的使用才能确定。

于 2011-05-10T18:43:26.597 回答
0

减少复制的明显方法是使用 shared_ptr 之类的东西。然而,使用多线程,这种治疗方法可能比疾病更糟糕——增加和减少引用计数需要原子地完成,这可能非常昂贵。但是,如果您通常最终修改副本并且需要每个副本都具有唯一性(即,修改副本不会影响原始副本),那么您最终可能会获得更差的性能,为引用计数的原子增量/减量付费,而且还是做了很多副本。

有几种明显的方法可以避免这种情况。一种是移动独特的对象而不是完全复制——如果你能让它工作,那就太好了。另一种是大部分时间使用非原子引用计数,并且仅在线程之间移动数据时才进行深拷贝。

不过,没有一个答案是普遍且非常干净的。

于 2011-05-10T19:21:12.223 回答