在没有dynamic_cast的大层次结构中确定基指针的真实类型

Posted

技术标签:

【中文标题】在没有dynamic_cast的大层次结构中确定基指针的真实类型【英文标题】:Determine real type of base pointer in a big hierarchy without dynamic_cast 【发布时间】:2018-07-27 16:10:07 【问题描述】:

假设我有一个抽象基类State 和至少两个派生类AnimalStatePlantState(也是抽象的)。另外,我有很多来自AnimalStatePlantState 的派生类。

class State // abstract
class AnimalState: public State // abstract
class PlantState: public State // abstract
//maybe few more of such classes here

class AnimalStateSpecific1: public AnimalState
class AnimalStateSpecific2: public AnimalState
... //many of them
class PlantStateSpecific1: public PlantState
class PlantStateSpecific2: public PlantState
... //many of them

现在假设,我在某种基于State 指针的方法中使用它们。随着时间的推移,这些指针被替换为指向来自State 层次结构的不同类的其他指针。它按照某种规则发生,特别是在预定义的状态图中。

现在到问题部分。为了确定下一个状态,我需要知道前一个状态。但是由于我只有基本的State 指针,我无法有效地判断我拥有什么类型的状态,而不对层次结构中的每个不好的派生类执行dynamic_cast。我可以拥有一些 enum 与我拥有的各种状态,但我不太喜欢这样,因为我不想混合来自两个层次结构分支的信息,因为它确实不同。此外,我不喜欢层次结构中的每个分支都使用不同的enums,例如AnimalStateEnumPlantStateEnum 等。

这个问题的最佳解决方案是什么?也许我的设计从一开始就不好?如果可能的话,我想让它尽可能通用,并且只使用基类对象。

【问题讨论】:

如果您真的需要知道State* 的实际类型,您可能会错过界面中的某些内容。 您可以使用double-dispatch 或为std::type_index 的类型生成索引。 您可能对我一直在为 STTCL 使用的设计模型感兴趣,您也可以将其用作框架,从 Harel (UML) 状态图中对您的状态机进行建模。跨度> 【参考方案1】:

现在到问题部分。为了确定下一个状态,我需要知道前一个。

基于我们拥有的有限信息的最简单解决方案 - 对象,它知道自己的状态创建下一个状态对象:

class State
public:
    ...
    virtual std::unique_ptr<State> transform( some data ) = 0;
;

然后你在每个派生自State 的类中实现它,它可以改变它的状态并知道它可以移动到哪里。您需要传递哪些数据不是一个简单的问题 - 它取决于您的任务并且可能有各种选项,但是您需要定义所有派生类都可以使用的东西,因为签名在基类上定义并在所有类上共享派生的。

这个问题的最佳解决方案是什么?也许我的设计一开始就不好?

这个问题不是微不足道的,只有对你的任务有相当深入的了解才能回答。如果您不确定 - 实施原型并检查解决方案是否适合您的问题。不幸的是,学习如何创建一个好的设计的唯一方法是你自己的经验(当然除了琐碎的案例)。

【讨论】:

这个对我来说看起来很有前途。谢谢,我会试试你的答案。 我在尝试了不同的方法后选择了这个。此外,所有的 cmets 对问题的理解都非常有帮助。谢谢!【参考方案2】:

你可以在状态类层次结构中简单地有一个虚拟方法next(), 然后执行类似于以下示例的操作:

State *globalState = nullptr;
void foo(State *s)

    globalState = s->next();

每个派生类都会按照自己的含义实现next()

PlantStateSpecific1 *AnimalStateSpecific1::next() return new PlantStateSpecific1; 
AnimalStateSpecific1 *PlantStateSpecific1::next() return new AnimalStateSpecific1; 

这比拥有派生类的枚举/整数描述符更具 OOP。

【讨论】:

您几乎不应该将原始指针返回到动态分配的内存。 @Slava 我同意。这只是一个单行 POC 实现来展示这个想法。【参考方案3】:

你可以在基本状态类中拥有一个整数,它下面的每个类都将在其构造函数中设置。然后,您可以使用常量序列、可能状态列表,其 id 对应于状态类型索引,或者使用枚举器。 id 更灵活,因为您可以相对轻松地创建状态类型并轻松地添加处理它们,以及如果您想从 id 类型创建新状态。

这只是 iv 以前做过的一种方式,但可能还有很多其他方式。

【讨论】:

我在我的问题中写道,我不喜欢枚举的想法,因为所有的状态都会被混合到一个大列表中。

以上是关于在没有dynamic_cast的大层次结构中确定基指针的真实类型的主要内容,如果未能解决你的问题,请参考以下文章

列出给定类的层次结构中的所有基类?

Python:子类整个层次结构

C++笔记-类层次结构

如何使用 unique_ptr 执行 dynamic_cast?

为啥 C-style cast 的行为与 dynamic_cast 不同?

C++强制类型转换操作符 dynamic_cast