【问题标题】:How to create a generic container with unknown member functions?如何创建具有未知成员函数的通用容器?
【发布时间】:2010-07-03 18:26:00
【问题描述】:

我注意到我经常需要一个容器类。例如,在处理粒子系统时,我创建了一个容器类Particles,它有一个成员vector<Particle*>。然后我调用:Particles* my_particlesmy_particles->draw() 一样,然后在Particles.draw() 中对vector<Particle*> 进行迭代,并在每个粒子上再次调用draw()。同样适用于update()addforce() 等成员函数。现在,我正在做一个项目,需要一个Cube 的集合,我需要调用tween()moveTowards() 等。

我知道我可以使用模板,但在模板类的情况下,成员函数需要事先知道。因为我想检查我是否可以创建一个通用类,例如我可以使用我的 Cubes 和 Particles 集合。

有谁做过这个或者可以给我一些建议吗?

亲切的问候, 波吕克斯

【问题讨论】:

  • 你为什么没有类似的东西:typedef std::vector<Particle> Particles 然后void draw(Particles& p) 等等?然后你可以做typedef std::vector<Cube> Cubesvoid tween(Cubes& c, ...) ...我认为这是更好的设计。
  • Polybos,我可以做到,但这不是我的问题的意思。虽然非常感谢您花时间回答!我认为斯蒂芬回答了我的问题!
  • 为什么要存储指针?如果您不存储对象本身,则会将自己暴露在内存泄漏中。如果您需要多态性,您可以使用boost::ptr_vector,它将为您管理内存。

标签: c++ algorithm stl containers


【解决方案1】:

简短的回答是你不能在 c++ 中做到这一点。但是,您可以使用 STL 算法和容器来封装这种行为。

首先,您需要将 Cube 或 Particle 实例放入 std::vector 或其他容器中(就像现在一样)。

然后您可以将 STL 的 std::for_eachstd::mem_fun 结合使用。

结果是这样的:

  std::vector<Particle*> V;

  V.push_back(new Particle);
  V.push_back(new Particle);
  V.push_back(new Particle);
  V.push_back(new Particle);

  std::for_each(V.begin(), V.end(), std::mem_fun(&Particle::draw));

【讨论】:

  • 嗨斯蒂芬!谢谢,我认为这正是我想要的,或者我认为这是最好的解决方案。这与使用迭代器和循环遍历粒子有什么区别?也许只有表演? (我可以想象使用自定义 for 循环会更快一些,这在使用数千个粒子时可能很重要)。还有一个问题,有没有办法传递参数?
  • 实际上,如果您正在调用函数,for_each 将与您的手写循环一样好甚至更快。 mem_fun 增加的开销可能可以忽略不计。传递参数很棘手,因为 for_each 只知道如何将对象本身作为参数传递;您需要创建一个类似函数的对象,该对象通过其他方式接收参数。 boost::bind 对这类事情有很大帮助,而且开销可能可以忽略不计。
  • @pollux :这可能与自定义 for 循环一样快,并且不太可能依赖于粒子的数量。编译器将能够生成非常相似的代码,但 for_each 可能不会被内联,从而导致多了一个函数调用。唯一可以确定的方法是基准测试。 WRT 传递参数,std::mem_fun 不能真正支持它,但 boost 库可以 boost.org/doc/libs/1_32_0/libs/bind/mem_fn.htmlboost.org/doc/libs/1_32_0/libs/bind/bind.html 一起使用。希望对您有所帮助。
  • 考虑到我们在这里有指向Particle 的指针,这项工作是否可行?你不需要引用来从 mem_fun on 调用函数吗?
  • @Matthieu :根据 STL 文档,它会。 mem_fun_ref 用于存储对象时。
【解决方案2】:

不确定我是否完全理解,但for_each STL 算法会有帮助吗? http://www.sgi.com/tech/stl/for_each.html

【讨论】:

  • 谢谢!正如斯蒂芬也建议的那样,我认为这是我需要的,或者是最好的解决方案。
【解决方案3】:

我读到您在问:“我能否制作一个可用于立方体和粒子的通用容器,即使它们具有不同的成员函数?”当然,这很容易。如果需要,您甚至可以将立方体 粒子放在同一个容器中。如果你正在处理指针,你只需使用void*

std::vector<void*> objects;
objects.push_back(new Particle(...));
objects.push_back(new Cube(...));

当然,你可以用 void*s 做很多事情,除了把它们扔回去:

for (i = objects.begin(), i != objects.end(), ++i) {
    void* p = objects[i];
    Particle* particle = dynamic_cast<Particle*>(p);
    if (particle) {
        // do particle stuff
        continue;
    }
    Cube* cube = dynamic_cast<Cube*>(p);
    if (cube) {
        // do cube stuff
    }
}

即使您只是将粒子存储在向量中,例如,您仍然需要向下转换才能使用它们:

for (i = objects.begin(), i != objects.end(), ++i) {
    void* p = objects[i];
    Particle* particle = dynamic_cast<Particle*>(p);
    if (particle) {
        // do particle stuff
    } else {
        // error!! I thought someone told me this thing only had Particles...
    }
}

您可以看到,这样做比仅将它们存储在单独的向量中要尴尬得多,您知道每个向量中每个对象的类型,并且不必执行运行时向下转换来处理它们。这就是为什么这种容器通常被认为是糟糕的风格。

您可以在此区域查看的其他可能性是 boost::anyboost::variant,它们可用于指针以外的其他事物。

【讨论】:

  • 嗨欧文,这不是我的意思。我不想创建一个容器,然后在循环中检查它们的类型。虽然很高兴看到这是可能的。
【解决方案4】:

据我了解,这里有一个设计问题,您想使用相同的接口迭代不同的对象类别(Cube/Particle),但很明显它们不能共享相同的接口,如果你真的想要那个,你必须在 Particles 和 Cube 的抽象基类上实现 tween() 和 moveTowards(),并且在 Particles 类中什么都不实现。

【讨论】:

  • 我不认为他试图将粒子和立方体放在同一个容器中,他只是想避免重复容器对象中的所有方法。
【解决方案5】:

为了完整起见,还有valarray,虽然使用vector/for_each 是更好的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-27
    • 1970-01-01
    • 2016-06-26
    相关资源
    最近更新 更多