c++ - 在C++中将不同的类按层次结构放在一个容器中

标签 c++ design-patterns oop stl

有时我们必须将不同的对象放在一个容器中的同一层次结构中。我读了一些文章说有一些技巧和陷阱。但是,我对这个问题没有大局观。实际上,这种情况在现实生活中经常发生。

例如,一个 parking 场必须停放不同类型的汽车;动物园必须包含不同类型的动物;书店必须包含不同类型的书籍。

我记得有一篇文章说下面这两种都不是好的设计,忘记是哪里了。

vector<vehicle> parking_lot;
vector<*vehicle> parking_lot;

任何人都可以为这类问题提供一些基本规则吗?

最佳答案

我在写对 a similar question 的回复时学到了很多东西同一作者,所以我忍不住在这里做同样的事情。简而言之,我编写了一个基准来比较以下方法来解决在标准容器中存储异构元素的问题:

  1. 为每种类型的元素创建一个类,让它们都继承自一个公共(public)基类,并将多态基类指针存储在 std::vector<boost::shared_ptr<Base> > 中。 .这可能是更通用和灵活的解决方案:

    struct Shape {
        ...
    };
    struct Point : public Shape { 
        ...
    };
    struct Circle : public Shape { 
        ...
    };
    std::vector<boost::shared_ptr<Shape> > shapes;    
    shapes.push_back(new Point(...));
    shapes.push_back(new Circle(...));
    shapes.front()->draw(); // virtual call
    
  2. 与 (1) 相同,但将多态指针存储在 boost::ptr_vector<Base> 中.这有点不太通用,因为元素仅由 vector 拥有,但在大多数情况下应该足够了。 boost::ptr_vector的优势之一|是它有一个std::vector<Base>的接口(interface)(没有 *),因此使用起来更简单。

     boost::ptr_vector<Shape> shapes;    
     shapes.push_back(new Point(...));
     shapes.push_back(new Circle(...));
     shapes.front().draw(); // virtual call
    
  3. 使用可以包含所有可能元素的 C union ,然后使用 std::vector<UnionType> .这不是很灵活,因为我们需要提前知道所有元素类型(它们被硬编码到 union 中),而且众所周知 union 不能与其他 C++ 结构很好地交互(例如,存储的类型不能有构造函数)。

    struct Point { 
        ...
    };
    struct Circle { 
        ...    
    };
    struct Shape {
        enum Type { PointShape, CircleShape };
        Type type;
        union {
            Point p;
            Circle c;
        } data;
    };
    std::vector<Shape> shapes;
    Point p = { 1, 2 };
    shapes.push_back(p);
    if(shapes.front().type == Shape::PointShape)
        draw_point(shapes.front());
    
  4. 使用 boost::variant可以包含所有可能的元素,然后使用 std::vector<Variant> .这不像 union 那样灵活,但处理它的代码要优雅得多。

    struct Point { 
        ...
    };
    struct Circle { 
        ...
    };
    typedef boost::variant<Point, Circle> Shape;
    std::vector<Shape> shapes;
    shapes.push_back(Point(1,2));
    draw_visitor(shapes.front());   // use boost::static_visitor
    
  5. 使用 boost::any (可以包含任何内容)然后是 std::vector<boost::any> .这非常灵活,但界面有点笨拙且容易出错。

    struct Point { 
        ...
    };
    struct Circle { 
        ...
    };
    typedef boost::any Shape;
    std::vector<Shape> shapes;
    shapes.push_back(Point(1,2));
    if(shapes.front().type() == typeid(Point))    
        draw_point(shapes.front());   
    

This is the code完整的基准程序(由于某种原因不在键盘上运行)。这是我的性能结果:

time with hierarchy and boost::shared_ptr: 0.491 microseconds

time with hierarchy and boost::ptr_vector: 0.249 microseconds

time with union: 0.043 microseconds

time with boost::variant: 0.043 microseconds

time with boost::any: 0.322 microseconds

我的结论:

  • 使用 vector<shared_ptr<Base> >仅当您需要运行时多态性提供的灵 active 并且您需要共享所有权时。否则,您的开销会很大。

  • 使用 boost::ptr_vector<Base>如果您需要运行时多态性但不关心共享所有权。它将明显快于 shared_ptr对应和界面将更加友好(存储元素不会像指针一样呈现)。

  • 使用 boost::variant<A, B, C>如果你不需要太多的灵 active (即你有一小组不会增长的类型)。它会很快,代码会很优雅。

  • 使用 boost::any如果您需要完全的灵 active (您想存储任何东西)。

  • 不要使用 union 。如果你真的需要速度,那么boost::variant一样快。

在结束之前我想提一下 std::unique_ptr 的 vector 当它广泛可用时将是一个不错的选择(我认为它已经在 VS2010 中了)

关于c++ - 在C++中将不同的类按层次结构放在一个容器中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2261366/

相关文章:

javascript - 具有 jQuery 和完整 JS 支持的 Dreamweaver 最新版本是什么?

c++ - 使用一个函数调用 C++ 初始化多个常量类成员

c++ - 内存地址相同但值不同的指针

python - 在python中实现协程

Java:类在包中找不到彼此(工厂设计模式)

design-patterns - 过度使用工厂模式

java - 无法访问内部类变量

c++ - 我们能否创建一个类似 scanf 的函数,在填充所有参数时返回 true,否则返回 false,而无需遍历所有变量?

c++ - 根据模板变量类型执行不同的方法

oop - TPL DataFlow 和架构设计