Неправильная сортировка набора объектов - PullRequest
0 голосов
/ 28 января 2019

Когда я печатаю весь набор, результат не сортируется И он содержит один дубликат.Объект Person имеет фамилию, фамилию и год рождения (все 3 строки).Сначала я сортирую по году рождения, затем по фамилии, а затем по фамилии.По сути, нет идентичных лиц (но даже если бы это было так, их следует исключить, когда они вставляются в set).

Чтобы быть более конкретным, я создаю набор таких людей, как этот:

std::set <Person> greatUncles; 

и вставьте их так:

greatUncles.insert(Person("bla", "bla", "1900"));

Вот основные вещи из класса Person:

class Person {
public:
  //...

  Person(std::string s, std::string f, std::string y)
    :surname(s), familyname(f), yearOfBirth(y)
  {
  }

  //...

  std::string getSurname() const {
    return surname;
  }

  std::string getFamilyname() const {
    return familyname;
  }

  std::string getYearOfBirth() const {
    return yearOfBirth;
  }

private:
  std::string surname;
  std::string familyname;
  std::string yearOfBirth;
};

//to print the set, overload the '<<' operator
std::ostream &operator<<(std::ostream &o, const Person &person) {
  o << person.getSurname() << " "
    << person.getFamilyname() << " "
    << person.getYearOfBirth() << std::endl;
  return o;
}

//to order the set, overload the '<' operator
bool operator< (Person const &p1, Person const &p2) {
  int compareYearOfBirth = p1.getYearOfBirth().compare(p2.getYearOfBirth());

  if (compareYearOfBirth == 0) {
    int compareFamilyname = p1.getFamilyname().compare(p2.getFamilyname());
    if (compareFamilyname == 0) {
      return p1.getSurname().compare(p2.getSurname());
    } else
      return compareFamilyname;
  } else
    return compareYearOfBirth;
}

и вот как явыведите набор двоюродных дедов:

void printGreatUncles(std::set <Person> &greatUncles) {
    std::ofstream outputFile;
    outputFile.open("greatuncle.dat");

    if (outputFile.is_open()) {
      for(Person const & person:greatUncles) {
        outputFile << person;
      }
      outputFile.close();
    }
  }

Теперь вывод в определенном случае должен выглядеть так (отсортировано по году):

Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967

, но выглядит это так:

Archibald Furtweger 1967
Sebastian Furtweger 1942
Nikolaus Furtweger 1951
Archibald Furtweger 1967

Я не могу себе представить, что (что) я делаю неправильно.

Ответы [ 2 ]

0 голосов
/ 28 января 2019

std::set требует от компаратора строгого строгого упорядочения.Частично это если a < b == true, то b < a == false, но у вас этого нет.Давайте представим, что год рождения и фамилии совпадают, и отличаются только фамилии.В вашем примере вы бы вернули некоторое положительное или отрицательное число, которое конвертируется в true, поскольку только 0 равно false.Если вы запустите проверку в обратном направлении, вы получите противоположное значение в целом числе, но оно все равно приведет к true.

. Для исправления этого C ++ 11 предлагает std::tie, который вы можете использовать для построения std::tuple членов и его operator < созданы, чтобы поступать правильно.Это делает ваш код похожим на

bool operator< (Person const &p1, Person const &p2) {
  return std::tie(p1.getYearOfBirth(), p1.getFamilyname(), p1.getSurname()) < 
         std::tie(p2.getYearOfBirth(), p2.getFamilyname(), p2.getSurname());
}

Если вы когда-нибудь захотите сделать это в будущем и сможете использовать C ++ 20, вы можете добавить к Person

auto operator<=>(const Person&) const = default;

иэто автоматически даст вам операторы ==,! =, <, <=,> и> = для Person, и они будут «делать правильные вещи», если вы хотите, чтобы все члены сравнивались в порядке, в котором они определеныкласс.

0 голосов
/ 28 января 2019

Вы возвращаете int, возвращаемое std::string::compare как bool.Это не то, что вам нужно, так как 1 и -1 конвертируются в true.

. Правильный код сравнения:

//to order the set, overload the '<' operator
bool operator< (Person const &p1, Person const &p2) {
  int compareYearOfBirth = p1.getYearOfBirth().compare(p2.getYearOfBirth());

  if (compareYearOfBirth == 0) {
    int compareFamilyname = p1.getFamilyname().compare(p2.getFamilyname());
    if (compareFamilyname == 0) {
      return p1.getSurname().compare(p2.getSurname()) < 0;
    } else
      return compareFamilyname < 0;
  } else
    return compareYearOfBirth < 0;
}

Параметр std::tieNathanOliver значительно менее подвержен ошибкам, чем описанный выше (хотя вы все равно можете легко испортить любой скопированный материал - я уже делал это раньше).

В C ++ 20 будет еще более простое решение(см. cppreference ):

class Person {
  // ...
public:
  auto operator<=>(const Person &) const = default;
}

Это в основном обеспечит все сравнения точно так же, как если бы вы вручную реализовали их через сравнения всех элементов std::tie d вместе.

...