Сортировать список указателей - PullRequest
6 голосов
/ 31 марта 2010

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

В любом случае, у меня есть 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

Пока здесь все в порядке. Однако я ввел некоторые производные классы:

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 объектов, используйте его id s, как и раньше.
  3. Для сравнения Child объектов, сравните его var s. Если они равны, отступите к правилу 2.
  4. GrandChild объекты должны вернуться к поведению Child (правило 3).

Сначала я подумал, что, возможно, смогу сделать несколько приведений в Comparator. Однако это отбрасывает постоянство. Тогда я подумал, что, вероятно, я мог бы сравнить typeid с, но тогда все выглядело грязно и это даже не правильно.

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

Спасибо

Ответы [ 6 ]

12 голосов
/ 31 марта 2010

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

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

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

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

1 голос
/ 01 апреля 2010

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

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

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

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

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

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

0 голосов
/ 31 марта 2010

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

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 класс

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 голосов
/ 31 марта 2010

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

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

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

0 голосов
/ 31 марта 2010
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 голосов
/ 31 марта 2010

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

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

Тогда класс Child может переопределить это поведение и заменить 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 *, без приведения.

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

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