【问题标题】:Sort a list of pointers对指针列表进行排序
【发布时间】:2011-02-03 00:11:13
【问题描述】:

我再次发现自己在 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对象cBaseObjectbb&lt;c
  2. 要比较 BaseObject 对象,请像以前一样使用其 ids。
  3. 要比较Child 对象,请比较其vars。如果它们相等,则回退到规则 2。
  4. GrandChild 对象应回退到 Child 行为(规则 3)。

我最初认为我可以在Comparator 中做一些演员表。然而,这抛弃了 constness。然后我想可能我可以比较typeids,但后来一切看起来都很乱,甚至都不正确。

我怎样才能实现这种排序,仍然使用list&lt;BaseObject*&gt;::sort

谢谢

【问题讨论】:

  • 我理解正确吗?首先,B

标签: c++ sorting stl pointers


【解决方案1】:

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

我可以对您的代码做一些观察吗?这并没有什么问题,但是:

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

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

【讨论】:

  • 我选择了std::list,因为我想快速擦除其中的元素。您能否详细说明“受保护的数据是个坏主意”?
  • 双重调度在 Scott Meyers 更有效的 C++amazon.com/exec/obidos/ASIN/020163371X/ref=nosim/bookspree-20987654322@中进行了解释
  • @YuppieNetworking 受保护的数据使您的派生类变得依赖于基础的实现细节,从而使更改变得困难。而且从来没有必要。有关我对名单的进一步抨击,请参阅punchlet.wordpress.com/2009/12/27/letter-the-fourth
  • 除非您的list 很大,否则vector 即使在中间删除/插入也会更快。不过,您可能会担心迭代器失效。
  • @sbi:我考虑过 :) 但无论如何我不建议选择性能,最好选择接口/保证。
【解决方案2】:

让对象在虚拟方法中提供排序键,默认为 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】:
    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】:

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

      如果对象本身应该知道如何相互排名,并且您很确定不会派生更多具有不同规则的类,我可能会在基础上添加几个虚函数名为“int primarySortKey()”和“int secondarySortKey()”的类。我会在比较器函数中使用这些。

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

      【讨论】:

        【解决方案5】:

        我只有一个问题:能够按特定顺序对它们进行排序是否重要,或者您可以按类型(以任何顺序)然后按类型中的键对它们进行排序吗?

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

        如前所述,唯一的问题是您无法控制哪个类别最低。这将需要一些模板魔法(例如),但这有那么重要吗?

        【讨论】:

          【解决方案6】:

          我可能在问题中遗漏了一些基本内容,看来您基本上是在尝试进行 2 级排序:

          • 1、基于类/对象类型:B

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

          如果是这样,有很多方法可以给猫剥皮,但为什么不创建一个所有类都覆盖的虚函数(例如 Key())?

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

          如果这是对问题的正确理解,也许像 this code sample 这样的东西对你有用?

          【讨论】:

          • 谢谢 Dan,代码不错。无论如何,我采取了其他方向......但这个答案教会了我一两件事......
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-05-16
          • 2013-11-02
          • 1970-01-01
          • 2014-04-12
          相关资源
          最近更新 更多