通过值传递,没有与std :: vector分配的深层副本?

时间:2012-01-05 18:49:08

标签: c++ vector set

了解std::set.insert& std::vector行为。 请考虑以下情况:

A.H

class A {
  uint id;
  vector<double> values;
  operator<(const A& argA) const;
}

A.cpp

A::A(uint argId, vector<double> argValues) {
    this->id = argId;
    this->values = argValues;
}

A::operator<(const A& argA) const {
    // it's guaranteed that there's always at least one element in the vector
    return this->values[0] < argA.values[0];
}

B.cpp

std::set<A> mySet;
for (uint i = 0; i < (uint) 10; i++)
{
  vector<double> tempVector(3);
  for (uint j = 0; j < (uint) 3; j++) {
    tempVector[j] = j;
  }

  myset.insert(A(i + 1, tempVector));
}

根据我的理解,tempElement拥有深度复制的向量(值),因为vector在其构造函数中通过值传递并分配。因此,循环遍历i不应该将添加的元素分解为我的集合。但是插入*tempElement个中断 - SIGSEV。在我的逻辑中,这应该工作......每一个帮助都赞赏!

编辑:代码在插入过程中崩溃(第二个元素); set调用LT运算符,尝试访问传递参数的向量 - 但不能。在创建A之前,我传递了id和向量,我检查传递的向量是否包含正确的元素。

4 个答案:

答案 0 :(得分:1)

对于一个小向量,它应该没关系,但是如果你有一个大数组并且继续复制它会很昂贵,那么你的A应该包含一些浅拷贝的指针。有几种选择:

  1. boost::shared_array<double>
  2. boost::shared_ptr<vector<double> >
  3. boost::shared_ptr<double>但是在构造中传入了数组删除器。
  4. 使A不可复制,并有一组(共享)指针指向A,并使用一些比较函数来比较指针中的内容而不是指针本身。
  5. 请注意,使用shared_arrayshared_ptr,您将无法提取大小(元素数量),因此您必须单独存储它。

答案 1 :(得分:0)

我认为问题不在于此代码中。但是我注意到你有一个矢量tempVector,但你将值赋给tempComponents。我看不到tempComponents声明,但我的猜测是它的大小不同。

答案 2 :(得分:0)

不,没有理由在此插入myset导致崩溃。问题必须在其他地方。如果你没有使用默认版本,也许在A的副本中。

但是你的代码泄漏了内存。当您插入集*tempElement时,会将new复制到集合中,然后不再使用您使用A tempElement(i+1,tempVector);分配的原始内容,但永远不会删除。相反,您可以只执行myset.insert(A(i+1,tempVector)),以便在将对象复制到集合中后,它会被正确销毁。或者在这种情况下可能更好,你可以将它构造为临时传递给insert:myset.emplace(i+1,tempVector);,在这种情况下,对象将被移动而不是复制,从而减少了开销。或者您可以构建对象以避免移动:tempComponents[j] = j;

此外,我假设您tempVector[j] = j表示std::iota(begin(tempVector),end(tempVector),0)。您可以用vector<double> tempVector(3) = {0.0,1.0,2.0} std::set<A> mySet; for (uint i = 0; i < (uint) 10; i++) { myset.emplace(i+1,tempVector); } 替换该循环。 编辑:或者您可以使用新的初始化程序语法此外,由于每次只使用循环外的向量,向量是相同的:

emplace

C ++ 03编译器不支持iota或新的初始化语法,而double tempVector_init[] = {0.0,1.0,2.0}; vector<double> tempVector(tempVector_init,tempVector_init+3); std::set<A> mySet; for (uint i = 0; i < (uint) 10; i++) { myset.insert(A(i+1,tempVector)); } 将是它们的编译器扩展(它来自原始的SGI STL,所以有些人可能会这样做。对于那些你仍然会使用insert并使用for循环初始化tempVector或使用数组:

{{1}}

答案 3 :(得分:0)

工作代码有很多变化 - 但我没有看到你所描述的问题。

#include <set>
#include <vector>

using namespace std;

typedef unsigned int uint;

class A {
public:
  A(uint argId, vector<double> argValues) 
  {
    this->id = argId;
    this->values = argValues;
  }

  bool operator < ( A const& a ) const 
  { 
    return a.id < id;
  }

  uint id;
  vector<double> values;
};


int _tmain(int argc, _TCHAR* argv[])
{

  std::set<A> mySet;
  for (uint i = 0; i < (uint) 10; i++)
  {
    vector<double> tempVector(3);
    for (uint j = 0; j < (uint) 3; j++) {
      tempVector[j] = j;
    }

    std::unique_ptr<A> tempElement(new A(i + 1, tempVector));
    mySet.insert(*tempElement);
  }

  return 0;
}