我再一次发现自己在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() {};
};
所以现在我想按照以下规则排序:
Child
对象c
和BaseObject
b
,b<c
BaseObject
个对象,请像以前一样使用其id
。Child
个对象,请比较其var
个。如果它们相等,则回退到规则2。GrandChild
个对象应该回退到Child
行为(规则3)。我最初认为我可以在Comparator
中进行一些演员表演。但是,这会消除常数。然后我想我可能比较typeid
s,但是一切看起来都很混乱,甚至都不正确。
如何使用list<BaseObject*>::sort
?
谢谢
答案 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)
}
如上所述,唯一的问题是您无法控制哪个类别最低。这需要一些模板魔法(例如),但它是否重要?