影响指针集合排序的因素(具体示例)

What affects ordering in my set of pointers (specific example)

本文关键字:集合 指针 排序 影响      更新时间:2023-10-16

在下面的测试代码中,指针显然是按照集合中的地址排序的,如打印输出所示。在不改变对象插入顺序的情况下,什么可以改变集合中的顺序,使id不按顺序打印?

谁能通过修改这段代码来展示一个例子?

#include <iostream>
#include <set>
class Object {
    int id;
  public:
    void setId (int i) {id = i;}
    int getId() {return id;}
};
int main ()
{
  std::set<Object*> myset1;
  std::set<Object*> myset2;
  std::set<Object*>::iterator it;
  Object *obj;
  for (int i=1; i<=5; ++i){
    obj = new Object();
    obj->setId(i);
    myset1.insert(obj);
  }
  for (int i=1; i<=5; ++i){
    obj = new Object();
    obj->setId(i);
    myset2.insert(obj);
  }
  std::cout << "myset1 contains:";
  for (it=myset1.begin(); it!=myset1.end(); ++it)
    std::cout << ' ' << (*it)->getId() << "(" << (long long)*it << ")";
  std::cout << 'n';
  std::cout << "myset2 contains:";
  for (it=myset2.begin(); it!=myset2.end(); ++it)
    std::cout << ' ' << (*it)->getId() << "(" << (long long)*it << ")";
  std::cout << 'n';
  return 0;
}
输出:

myset1 contains: 1(24842256) 2(24842336) 3(24842416) 4(24842496) 5(24842576)
myset2 contains: 1(24842656) 2(24842736) 3(24842816) 4(24842896) 5(24842976)

您的问题有点模糊,因为不清楚您是在为未说明的问题寻求比较器更改解决方案,还是只是想知道相同的比较器和相同的插入顺序是否有可能生成您所看到的排序。大多数其他答案似乎都是基于前者;

std::set<>容器默认使用std::less<>作为比较器。对于任何指针类型,std::less<>提供了基于总内存排序的专门化(如您所见)。

你完全任由内存分配器摆布。例如,考虑对第一个循环进行以下代码更改:

char *tmp = new char[sizeof(Object)];
for (int i=1; i<=5; ++i){
    obj = new Object();
    obj->setId(i);
    myset1.insert(obj);
    if (i == 3)
        delete [] tmp; // open the hole up after the third insertion
}

在我的设备上(OS X 10.10.5),它给出以下输出:

myset1 contains: 4(4296037184) 1(4296037200) 2(4296037264) 3(4296037328) 5(4296037440)
myset2 contains: 1(4296037504) 2(4296037568) 3(4296037632) 4(4296037696) 5(4296037760)

我可以推测是什么引起的。这个初始临时分配一旦被释放,就会在堆管理器的自由链中添加一个空闲块,这个块的大小(并非巧合)正好适合循环中消耗的下一个分配(第四个)。

不知道这是不是你想要的,但最重要的是,你的代码确实完全受内存管理器的支配,它决定了对象占用的地址,从而决定了它们在集合中的顺序。

默认情况下,set中的元素按指针排序。不能保证以什么顺序获得这些指针(是的,它们有可能是顺序的,但没有这样的保证)。如果你想要稳定的顺序,你需要添加比较函子。另一种方法是创建vector并使用Object指针指向vector的元素,它们将按顺序排列:

struct ObjectLess : public std::binary_function<Object, Object, bool> {
    bool operator()(const Object* lhs, const Object* rhs) const
    {
        return lhs->getId() < rhs->getId();
    }
};

请注意,我已将const添加到getId:

#include <iostream>
#include <set>
class Object {
    int id;
  public:
    void setId (int i) {id = i;}
    int getId() const {return id;}
};
struct ObjectLess : public std::binary_function<Object, Object, bool> {
    bool operator()(const Object* lhs, const Object* rhs) const
    {
        return lhs->getId() < rhs->getId();
    }
};
int main ()
{
  std::set<Object*, ObjectLess> myset1;
  std::set<Object*, ObjectLess> myset2;
  std::set<Object*, ObjectLess>::iterator it;
  Object *obj;
  for (int i=1; i<=5; ++i){
    obj = new Object();
    obj->setId(i);
    myset1.insert(obj);
  }
  for (int i=1; i<=5; ++i){
    obj = new Object();
    obj->setId(i);
    myset2.insert(obj);
  }
  std::cout << "myset1 contains:";
  for (it=myset1.begin(); it!=myset1.end(); ++it)
    std::cout << ' ' << (*it)->getId() << "(" << (long long)*it << ")";
  std::cout << 'n';
  std::cout << "myset2 contains:";
  for (it=myset2.begin(); it!=myset2.end(); ++it)
    std::cout << ' ' << (*it)->getId() << "(" << (long long)*it << ")";
  std::cout << 'n';
  return 0;
}
输出:

myset1 contains: 1(105690555281392) 2(105690555281360) 3(105690555281328) 4(105690555281296) 5(105690555281264)
myset2 contains: 1(105690555281232) 2(105690555281200) 3(105690555281168) 4(105690555281136) 5(105690555281104)

您不能更改std::set的默认行为,除非为它提供比较器,并在其中定义您自己的比较逻辑来控制顺序,例如:

struct MyCom {
    bool operator()(Object* o1, Object* o2) const { return o2->getId() < o1->getId(); }
};

std::set<Object*, MyCom> myset2;

现在myset2中的元素将按照Object::id的desc顺序排序:

myset2包含:5(144501624)4(144501584)3(144501544)2(144501504)1(144501464)

生活