提出正确的问题至少是获得好答案的一半。您应该真正说明您想要实现的目标,而不是您面临的特定问题。在我看来,您的语言问题似乎比您实际意识到的要多。
第一部分是您似乎有一个派生自 Component 的组件层次结构,可能提供了一个通用接口。一个实体在内部拥有许多组件,它们可以是任何从 Component 派生的类型。如果是这种情况,您应该在直接存储 Component 对象时重新设计您的容器,这将在您的对象中产生切片(无论您在容器中输入什么派生类型,容器都只会保留常见的 Component 部分目的)。
处理几个向量并希望它们始终保持同步是可行的,但很脆弱。如果名称和组件一起出现,那么您希望存储成对的名称/组件。如果要按名称搜索,则应使用地图,因为它将直接提供 O(log N) 搜索。
现在,回到问题。如果您想要实现的是简单的语法糖(如果需要,请避免调用者显式动态转换),那么您可以使用模板获得它(稍后会详细介绍)。但是你真的应该考虑你的设计。Component 是否定义了任何组件的真实接口?如果用户在使用组件之前需要向下转换为特定类型,则要么抽象不好(组件不提供真正的接口),要么对象并没有真正组合在一起。
如果在它结束时您仍然想这样做,您可以通过在模板方法(或自由函数)中执行它来向调用者隐藏动态转换。
class Entity {
typedef std::map< std::string, boost::shared_ptr<Component> > component_map_t;
public:
boost::shared_ptr<Component> getComponent( std::string const & name ) {
component_map_t::iterator it = components_.find(name);
if ( it == components_.end() ) { // not found, handle error
// ...
}
return it->second;
}
template <typename T> // syntactic sugar
boost::shared_ptr<T> getComponent( std::string const & name ) {
return boost::dynamic_pointer_cast<T>( getComponent(name) );
}
private:
component_map_t components_;
};
template <typename T> // syntactic sugar also available as free function
boost::shared_ptr<T> getComponent( Entity & entity, std::string const & name ) {
return boost::dynamic_pointer_cast<T>( entity.getComponent(name) );
}
int main() { // usage
Entity e; // ... work with it add components...
boost::shared_ptr<Component> c1 = e.getComponent( "one" ); // non-templated method returns Component
boost::shared_ptr<DerivedComponent> c2 = e.getComponent<DerivedComponent>( "two" );
boost::shared_ptr<DerivedComponent> c3 = getComponent<DerivedComponent>( e, "two" );
}
您可以使用界面,而不是boost::shared_ptr
返回真正的引用(它所需要的:必须仔细控制生命周期,以便如果从实体中删除组件,用户代码不会尝试使用悬空引用)。