对指针列表进行排序

时间:2010-03-31 15:53:04

标签: c++ sorting stl pointers

我再一次发现自己在C ++中完成了一些非常简单的任务。有时我希望我可以在Java中从OO中学习我所知道的所有内容,因为我的问题通常都是从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对象cBaseObject bb<c
  2. 要比较BaseObject个对象,请像以前一样使用其id
  3. 要比较Child个对象,请比较其var个。如果它们相等,则回退到规则2。
  4. GrandChild个对象应该回退到Child行为(规则3)。
  5. 我最初认为我可以在Comparator中进行一些演员表演。但是,这会消除常数。然后我想我可能比较typeid s,但是一切看起来都很混乱,甚至都不正确。

    如何使用list<BaseObject*>::sort

    实现此类型

    谢谢

6 个答案:

答案 0 :(得分:12)

您正在考虑进行双重调度 - 即根据两个对象的类型而不是一个对象来调用虚函数。看看这篇维基百科文章,了解单挑http://en.wikipedia.org/wiki/Double_dispatch。我必须说,每当我发现自己处于这种情况时,我会尝试改变方向: - )

我可以对您的代码做一些观察。它并没有完全错误,但是:

  • 在C ++中,std :: list是最后的容器 - 你通常应该默认使用std:; vector,除非你特别需要一个只有list提供的功能:

  • 受保护的数据总是一个坏主意

答案 1 :(得分:1)

我可能会遗漏一些问题的基本内容,看起来你基本上都在尝试进行两级排序:

  • 1,基于类/对象类型:B&lt; C&lt; ģ

  • 第二,在类似的对象中,你想使用id / var字段(GrandChild除外,它似乎没有这样的字段。)

如果是这种情况,有很多方法可以给猫皮肤,但为什么不创建一个所有类重写的虚函数(例如 Key())?

Key()可以返回 std :: pair ,第一个成员是指示类顺序的东西(可能是'b','c'之类的字符和'g',方便地按正确的顺序排列),第二个成员表示类中的等级/顺序(这将是类中的id / var数据成员)。 std :: pair 已经支持2级排序。

如果这是对问题的正确理解,那么像this code sample这样的东西可能对你有用吗?

答案 2 :(得分:0)

让对象在虚方法中提供排序键,默认为id:

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

比较器现在使用key()方法而不是直接访问id:

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 *指向的具体实例,没有强制转换。

编辑:哎呀,我刚刚理解你的问题就足以意识到这实际上并没有解决。见尼尔的答案。

答案 3 :(得分:0)

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;

继续自己:)

答案 4 :(得分:0)

我看到两种方法 - 哪种方法取决于你想如何思考这个问题(以及谁拥有两个对象中哪一个应该是第一个的想法)。

如果对象本身应该知道如何相互排名,并且你很确定你不会得到更多具有不同规则的类,我可能会向基础添加几个虚函数名为'int primarySortKey()'和'int secondarySortKey()'的类。我会在比较器函数中使用它们。

另一方面,如果对象不应该知道它们应该如何排序(并且比较器函数需要更多地了解对象,它们的含义和结构)我可能会找到一些方法来获取比较器中对象的类(通过反射,或通过引入类型的概念',并在比较器中写出一些扭曲的逻辑来确定要做什么。

答案 5 :(得分:0)

我只有一个问题:能够按照这个特定的顺序对它们进行排序很重要,或者你可以按类型(按任意顺序)排序,然后按类型中的键排序?

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

如上所述,唯一的问题是您无法控制哪个类别最低。这需要一些模板魔法(例如),但它是否重要?