如何创建异构对象集合? [英] How do I create a heterogeneous collection of objects?

查看:34
本文介绍了如何创建异构对象集合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 Vec 中使用特征对象.在 C++ 中,我可以创建一个基类 Thing,从中派生 Monster1Monster2.然后我可以创建一个 std::vector.Thing 对象必须存储一些数据,例如x : int, y : int,但是派生类需要添加更多的数据.

I want to use trait objects in a Vec. In C++ I could make a base class Thing from which is derived Monster1 and Monster2. I could then create a std::vector<Thing*>. Thing objects must store some data e.g. x : int, y : int, but derived classes need to add more data.

目前我有类似的东西

struct Level {
    // some stuff here
    pub things: Vec<Box<ThingTrait + 'static>>,
}

struct ThingRecord {
    x: i32,
    y: i32,
}

struct Monster1 {
    thing_record: ThingRecord,
    num_arrows: i32,
}

struct Monster2 {
    thing_record: ThingRecord,
    num_fireballs: i32,
}

我用get_thing_record()attack()make_noise()等方法定义了一个ThingTrait. 并为 Monster1Monster2 实现它们.

I define a ThingTrait with methods for get_thing_record(), attack(), make_noise() etc. and implement them for Monster1 and Monster2.

推荐答案

Trait 对象

实现对象的异构集合(在本例中为向量)的最可扩展方式正是您所拥有的:

Trait objects

The most extensible way to implement a heterogeneous collection (in this case a vector) of objects is exactly what you have:

Vec<Box<dyn ThingTrait + 'static>>

尽管有时您可能希望生命周期不是 'static,所以您需要类似的东西:

Although there are times where you might want a lifetime that's not 'static, so you'd need something like:

Vec<Box<dyn ThingTrait + 'a>>

你也可以有一组对特征的引用,而不是盒装特征:

You could also have a collection of references to traits, instead of boxed traits:

Vec<&dyn ThingTrait>

示例:

trait ThingTrait {
    fn attack(&self);
}

impl ThingTrait for Monster1 {
    fn attack(&self) {
        println!("monster 1 attacks")
    }
}

impl ThingTrait for Monster2 {
    fn attack(&self) {
        println!("monster 2 attacks")
    }
}

fn main() {
    let m1 = Monster1 {
        thing_record: ThingRecord { x: 42, y: 32 },
        num_arrows: 2,
    };

    let m2 = Monster2 {
        thing_record: ThingRecord { x: 42, y: 32 },
        num_fireballs: 65,
    };

    let things: Vec<Box<dyn ThingTrait>> = vec![Box::new(m1), Box::new(m2)];
}

BoxRc&dyn SomeTrait等都是trait对象.这些允许在无限数量的类型上实现 trait,但代价是它需要一定量的间接和动态调度.

Box<dyn SomeTrait>, Rc<dyn SomeTrait>, &dyn SomeTrait, etc. are all trait objects. These allow implementation of the trait on an infinite number of types, but the tradeoff is that it requires some amount of indirection and dynamic dispatch.

另见:

正如评论中提到的,如果您有固定数量的已知替代方案,那么一个不太开放的解决方案是使用枚举.这不需要对值进行 Boxed,但它仍然会有少量的动态调度来决定在运行时存在哪个具体的枚举变体:

As mentioned in the comments, if you have a fixed number of known alternatives, a less open-ended solution is to use an enum. This doesn't require that the values be Boxed, but it will still have a small amount of dynamic dispatch to decide which concrete enum variant is present at runtime:

enum Monster {
    One(Monster1),
    Two(Monster2),
}

impl Monster {
    fn attack(&self) {
        match *self {
            Monster::One(_) => println!("monster 1 attacks"),
            Monster::Two(_) => println!("monster 2 attacks"),
        }
    }
}

fn main() {
    let m1 = Monster1 {
        thing_record: ThingRecord { x: 42, y: 32 },
        num_arrows: 2,
    };

    let m2 = Monster2 {
        thing_record: ThingRecord { x: 42, y: 32 },
        num_fireballs: 65,
    };

    let things = vec![Monster::One(m1), Monster::Two(m2)];
}

另见:

这篇关于如何创建异构对象集合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆