Отсортируйте список указателей

Еще раз я перестал работать в некоторой действительно простой задаче в C++. Иногда мне жаль, что я не мог de-learn все, что я знаю от OO в Java, так как мои проблемы обычно запускаются путем размышления как Java.

Так или иначе у меня есть a std::list<BaseObject*> то, что я хочу отсортировать. Скажем, это BaseObject :

class BaseObject {
protected:
    int id;
public: 
    BaseObject(int i) : id(i) {};
    virtual ~BaseObject() {};
};

Я могу отсортировать список указателя на BaseObject со структурой компаратора:

struct Comparator {
    bool operator()(const BaseObject* o1, const BaseObject* o2) const {
        return o1->id < o2->id;
    }
};

И это было бы похоже на это:

std::list<BaseObject*> mylist;
mylist.push_back(new BaseObject(1));
mylist.push_back(new BaseObject(2));
// ...

mylist.sort(Comparator()); 

// intentionally omitted deletes and exception handling

До здесь, все - a-ok. Однако я представил некоторые производные классы:

class Child : public BaseObject {
    protected:
    int var;
    public: 
    Child(int id1, int n) : BaseObject(id1), var(n) {};
    virtual ~Child() {};
};

class GrandChild : public Child {
    public:
    GrandChild(int id1, int n) : Child(id1,n) {};
    virtual ~GrandChild() {};
};

Таким образом, теперь я хотел бы отсортировать после следующих правил:

  1. Для любого Child объект c и BaseObject b, b<c
  2. Выдерживать сравнение BaseObject объекты, используйте ids, как прежде.
  3. Выдерживать сравнение Child объекты, сравните vars. Если они равны, нейтрализация к правилу 2.
  4. GrandChild объекты должны нейтрализация к Child поведение (правило 3).

Я первоначально думал, что мог, вероятно, выполнить в некоторых бросках Comparator. Однако это выбрасывает constness. Затем я думал, что, вероятно, мог выдержать сравнение typeids, но затем все выглядело грязным, и это даже не корректно.

Как я мог реализовать этот вид, все еще с помощью list<BaseObject*>::sort ?

Спасибо

6
задан YuppieNetworking 31 March 2010 в 15:53
поделиться

6 ответов

Вы рассматриваете двойную отправку - то есть вызов виртуальной функции, зависящей от типов двух объектов, а не одного. Взгляните на эту статью в Википедии, чтобы получить предупреждение http://en.wikipedia.org/wiki/Double_dispatch . Я должен сказать, что всякий раз, когда я оказываюсь в такой ситуации, я пытаюсь изменить направление: -)

И могу я сделать пару замечаний по поводу вашего кода. В этом нет ничего плохого, но:

  • в C ++ std :: list является контейнером последней инстанции - обычно вы должны по умолчанию использовать вектор std:;, если вам специально не нужна функция, которая предоставляет только list:

  • защищенные данные - всегда плохая идея

12
ответ дан 8 December 2019 в 18:34
поделиться

Мне может не хватать чего-то базового в вопросе, похоже, вы в основном пытаетесь выполнить двухуровневую сортировку:

  • 1-й, на основе типа класса / объекта : B

  • 2-й, среди похожих объектов, вы хотите использовать поле id / var (за исключением GrandChild, у которого, похоже, нет такого поля).

Если это так, то есть Есть много способов снять шкуру с кошки, но почему бы не создать виртуальную функцию (например, Key () ), которую отменяют все классы?

Key () может вернуть std :: пара , первый член - это что-то, указывающее на порядок классов (возможно, такие символы, как 'b', 'c' и 'g', удобно уже в правильном порядке), второй член указывает ранг / порядок в пределах class (это будет член данных id / var внутри класса). std :: pair уже поддерживает двухуровневую сортировку.

Если это правильное понимание проблемы, может быть, вам подойдет что-то вроде , этот пример кода ?

1
ответ дан 8 December 2019 в 18:34
поделиться

Предоставить объекту ключ сортировки в виртуальном методе, по умолчанию это идентификатор:

class BaseObject {
protected:
    int id;
public: 
    BaseObject(int i) : id(i) {};
    virtual ~BaseObject() {};
    virtual int key() const { return id; }
};

Компаратор теперь использует метод key () вместо прямого доступа к идентификатору:

struct Comparator {
    bool operator()(const BaseObject* o1, const BaseObject* o2) const {
        return o1->key() < o2->key();
    }
};

Затем дочерний класс может переопределить это поведение и замените var как ключ сортировки:

class Child : public BaseObject {
protected:
    int var;
public: 
    Child(int id1, int n) : BaseObject(id1), var(n) {};
    virtual ~Child() {};
    int key() const { return var; }
};

Теперь ключ сортировки зависит от конкретного экземпляра, на который указывает BaseObject *, без приведения типов.

РЕДАКТИРОВАТЬ: Ой, я просто достаточно хорошо понял вашу проблему, чтобы понять, что это на самом деле не решает. См. Ответ Нила.

0
ответ дан 8 December 2019 в 18:34
поделиться
if (typeid(o1) == typeid(Child) && typeid(o2) == typeid(BaseObject))
   return true;
if (typeid(o2) == typeid(Child) && typeid(o1) == typeid(BaseObject))
   return false;
if (typeid(o1) == typeid(BaseObject) && typeid(o2) == typeid(BaseObject))
   return o1-> id < o2->id;

продолжайте:)

0
ответ дан 8 December 2019 в 18:34
поделиться

У меня только один вопрос: важно ли иметь возможность сортировать их в этом конкретном порядке, или вам будет удобно сортировать их по типу (в любом порядке), а затем по ключу внутри типа?

class BaseObject
{
public:
  static void* Category() { return typeid(BaseObject).name(); }
  virtual void* category() const { return Category(); }
  virtual int key() const { return mId; }

private:
  int mId; // would prefer a stronger type than int...
};

bool operator<(const BaseObject& lhs, const BaseObject& rhs)
{
  return lhs.category() <  rhs.category()
     || (lhs.category() == rhs.category() && lhs.key() < rhs.key());
}

class ChildObject: public BaseObject
{
public:
  static void* Category() { return typeid(ChildObject).name(); }
  virtual void* category() const { return Category(); }
  virtual int key() const { return mId; }
private:
  int mVar;
};

class GrandChildObject: public ChildObject
{
};

И ] Comparator class

struct Comparator
{
  bool operator<(const BaseObject* lhs, const BaseObject* rhs) const
  {
    // We would not want to dereference a null pointer by mistake now would we ?
    // Let's consider than a null pointer is < to a real object then :)
    return lhs ? ( rhs ? *lhs < *rhs : false ) : ( rhs ? true : false );
  }
};

Нет, вы не можете поместить BaseObject раньше ... но вы можете разбить его по категориям.

class HasCategory: public std::unary_function<const BaseObject*,bool>
{
public:
  explicit HasCategory(void* c): mCategory(c) {}
  bool operator()(const BaseObject* b) const { return b.category() == mCategory;}
private:
  void* mCategory;
};

int main(int argc, char* argv[])
{
  std::vector<const BaseObject*> vec = /* */;

  std::vector<const BaseObject*>::iterator it =
    std::partition(vec.begin(), vec.end(), HasCategory(ChildObject::Category()));

  // Now
  // [ vec.begin(), it [ contains only object of category ChildObject::Category()
  // [ it, vec.end() [ contains the others (whatever)
}

Единственная проблема, как уже упоминалось, заключается в том, что вы не контролируете, какая категория будет самой низкой. Для этого потребуется некоторая магия шаблонов (например), но так ли это важно?

0
ответ дан 8 December 2019 в 18:34
поделиться

Я вижу два подхода. Какой из них зависит от того, как вы хотите думать о проблеме (и кому принадлежит идея какого из двух объектов должен быть первым).

Если сами объекты должны иметь представление о том, как расположить друг друга, и вы почти уверены, что не собираетесь выводить больше классов с другими правилами, я бы, вероятно, добавил пару виртуальных функций в базу. класс с именами int primarySortKey () и int secondarySortKey (). Я бы использовал их в функции компаратора.

Если, с другой стороны, объекты не должны иметь представление о том, как они должны быть отсортированы (а функция компаратора должна знать гораздо больше об объектах, их значении и структуре), я, вероятно, нашел бы способ получить класс объекта в компараторе (либо посредством отражения, либо путем введения идеи типа »и напишите некоторую извращенную логику в компараторе, чтобы выяснить, что делать.

0
ответ дан 8 December 2019 в 18:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: