Передача по значению, нет глубокого копирования при назначении с std :: vector? - PullRequest
0 голосов
/ 05 января 2012

Понимание поведения std::set.insert & std::vector.Пожалуйста, рассмотрите следующий сценарий:

Ач

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 был передан по значению в своем конструкторе и назначен.Поэтому зацикливание не должно нарушать добавленные элементы в моем наборе.НО вставка *tempElement разрывов - СИГСЕВ.В моей логике это должно работать ... Любая помощь приветствуется!

РЕДАКТИРОВАТЬ: код падает во время процесса вставки (второй элемент);set вызывает LT-оператор, пытается получить доступ к вектору переданного аргумента - но не может.Перед созданием A, где я передаю id и вектор, я проверяю, содержит ли переданный вектор нужные элементы.

Ответы [ 4 ]

1 голос
/ 05 января 2012

Для маленького вектора это не имеет значения, но если у вас большой массив, и копировать его будет дорого, ваш A должен содержать какой-то указатель, который мелко копирует.Есть несколько опций:

  1. boost::shared_array<double>
  2. boost::shared_ptr<vector<double> >
  3. boost::shared_ptr<double>, но с удаленным массивом, переданным в конструкцию.
  4. Сделать A не подлежащим копированию и иметь набор (общих) указателей на A с некоторым функтором сравнения, который сравнивает то, что находится в указателях, а не сами указатели.shared_ptr вы не сможете извлечь размер (количество элементов), поэтому вам придется хранить его отдельно.
0 голосов
/ 05 января 2012

Рабочий код с многочисленными изменениями - но я не вижу проблемы, которую вы описывали.

#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;
}
0 голосов
/ 05 января 2012

Нет, для вставки в myset здесь нет причин вызывать сбой.Проблема должна лежать в другом месте.Возможно, в ctor копии А, если вы не используете файл по умолчанию.

Однако ваш код пропускает память.При вставке в набор *tempElement копируется в набор, а затем оригинал, выделенный с помощью new, больше не используется, но никогда не удаляется.Вместо этого вы можете просто сделать A tempElement(i+1,tempVector);, чтобы после копирования объекта в набор он был должным образом уничтожен.Или, может быть, лучше в этом случае вы могли бы просто создать его как временный элемент, передаваемый непосредственно для вставки: myset.insert(A(i+1,tempVector)), в этом случае объект будет перемещен вместо копирования, что уменьшит накладные расходы.Или вы можете просто построить объект на месте, чтобы избежать даже перемещения: myset.emplace(i+1,tempVector);

Также я предполагаю, что под tempComponents[j] = j; вы имели в виду tempVector[j] = j.Вы можете заменить этот цикл на std::iota(begin(tempVector),end(tempVector),0). edit: или вы можете использовать новый синтаксис инициализатора Кроме того, поскольку вектор всегда один и тот же, вы можете использовать только один вне цикла:

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);
}

C ++ 03 компиляторы победили '• поддерживает emplace или новый синтаксис инициализатора, и iota будет для них расширением компилятора (это из исходного SGI STL, так что некоторые могут его иметь).Для тех, кто по-прежнему будет использовать вставку и использовать цикл for для инициализации tempVector или использовать массив:

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));
}
0 голосов
/ 05 января 2012

Я не думаю, что проблема в этом коде.Однако я заметил, что у вас есть вектор tempVector, но вы вместо этого присваиваете значения для tempComponents.Я не вижу объявления tempComponents, но думаю, что оно другого размера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...