c++ - 从子类到父类(super class)再到子类的转换?

标签 c++ inheritance polymorphism

我的程序需要处理不同种类的“笔记”:NoteShort , NoteLong ...不同种类的笔记应该以不同的方式显示在 GUI 中。我为这些笔记定义了一个基类,叫做 NoteBase .

我将这些笔记存储在 XML 中;我有一个从 XML 文件读取并将笔记数据存储在 vector<NoteBase *> list 中的类.然后我发现我无法获取它们自己的类型,因为它们已经转换为 NoteBase * !

虽然if(dynamic_cast<NoteLong *>(ptr) != NULL) {...}可能有效,它真的太丑了。实现函数取 NoteShort *NoteLong *作为参数不起作用。那么,有什么好的方法可以解决这个问题吗?

更新:谢谢大家的回复。我认为这也不应该发生——但它确实发生了。我以另一种方式实现了它,它现在可以工作了。然而,据我所知,我确实在 NoteBase 中声明了(纯)虚函数。 , 但忘记在派生类的 header 中再次声明它。我想这就是导致问题的原因。

更新 2(重要): 我从 C++ Primer 中找到了这句话,这可能对其他人有帮助:

What is sometimes a bit more surprising is that the restriction on converting from base to derived exists even when a base pointer or reference is actually bound to a derived object:

 Bulk_item bulk;
 Item_base *itemP = &bulk;  // ok: dynamic type is Bulk_item
 Bulk_item *bulkP = itemP;  // error: can't convert base to derived

The compiler has no way to know at compile time that a specific conversion will actually be safe at run time. The compiler looks only at the static types of the pointer or reference to determine whether a conversion is legal. In those cases when we know that the conversion from base to derived is safe, we can use a static_cast (Section 5.12.4, p. 183) to override the compiler. Alternatively, we could request a conversion that is checked at run time by using a dynamic_cast, which is covered in Section 18.2.1 (p. 773).

最佳答案

这里有两个重要的思路和代码,所以最短的优先:


你可能不需要往回投。如果所有 Note 都提供统一的操作(比如 Chime),那么您可以简单地拥有:

class INote
{
    virtual void Chime() = 0;
};

...
for_each(INote * note in m_Notes)
{
    note->Chime();
}

并且每个音符都会使用内部信息(例如,持续时间和音调)按其应有的方式鸣响

这是干净、简单的,并且需要最少的代码。但是,这确实意味着所有类型都必须提供并继承自特定的已知接口(interface)/类。


现在,当您确实需要知道类型并转换回它时,就会出现更长、更复杂的方法。有两种主要方法和一种变体 (#2),可以与 #3 一起使用或结合使用:

  1. 这可以在带有 RTTI(运行时类型信息)的编译器中完成,允许它安全地 dynamic_cast 并充分了解允许的内容。但是,这仅适用于单个编译器和单个模块(DLL/SO/等)。如果您的编译器支持它并且 RTTI 没有明显的缺点,那么它是迄今为止最简单的,并且您的工作量最少。但是,它不允许类型识别自身(尽管 typeof 函数可能可用)。

    这是按照你的方式完成的:

    NewType * obj = dynamic_cast<NewType*>(obj_oldType);
    
  2. 为使其完全独立,向基类/接口(interface)添加虚方法(例如,Uuid GetType() const;)允许对象随时标识自己。与第三种(真正的 COM)方法相比,这有一个好处,但也有一个缺点:它允许对象的用户对要做什么做出智能且可能更快的决定,但需要 a) 他们转换(这可能需要和不安全的 reinterpret_cast 或 C 风格的转换)和 b) 该类型不能进行任何内部转换或检查。

    ClassID id = obj->GetType();
    if (id == ID_Note_Long)
        NoteLong * note = (NoteLong*)obj;
        ...
    
  3. COM 使用的选项是提供 RESULT/* success */CastTo(const Uuid & type, void ** ppDestination); 形式的方法。这允许类型 a) 在内部检查转换的安全性,b) 自行决定在内部执行转换(有关于可以做什么的规则)和 c) 如果转换不可能或失败则提供错误。但是,它 a) 会阻止用户表单优化,并且 b) 可能需要多次调用才能找到成功的类型。

    NoteLong * note = nullptr;
    if (obj->GetAs(ID_Note_Long, &note))
        ...
    

以某种方式组合后两种方法(例如,如果传递 00-00-00-0000 Uuid 和 nullptr 目标,用类型自己的 Uuid 填充 Uuid)可能是识别和安全转换类型的最佳方法。后一种方法以及它们的组合都是独立于编译器和 API 的,甚至可以谨慎地实现语言独立性(就像 COM 所做的那样,以合格的方式)。

ClassID id = ClassID::Null;
obj->GetAs(id, nullptr);
if (id == ID_Note_Long)
    NoteLong * note;
    obj->GetAs(ID_Note_Long, &note);
    ...

当类型几乎完全未知时,后两者特别有用:源库、编译器甚至语言都无法提前知道,唯一可用的信息是提供了给定的接口(interface)。使用如此少的数据并且无法使用高度特定于编译器的功能(如 RTTI),要求对象提供有关自身的基本信息是必要的。然后用户可以要求对象根据需要转换自己,并且对象可以完全决定如何处理。这通常与高度虚拟的类甚至接口(interface)(纯虚拟)一起使用,因为这可能是用户代码可能拥有的所有知识。

在您的范围内,此方法可能对您没有用,但您可能会感兴趣,并且对于类型如何识别自身并从基类或接口(interface)“向上”强制转换回类型而言肯定很重要。

关于c++ - 从子类到父类(super class)再到子类的转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7950711/

相关文章:

c++ - error LNK2019 : unresolved external symbol, 无法链接我编码的cpp文件

c++ - 在这种情况下如何避免代码重复问题?

c++ - Boost:以秒/毫/微/纳计算函数运行的时间

c++ - 基于模板的主题观察者模式——我应该使用 static_cast 还是 dynamic_cast

java - 为什么在覆盖时不允许缩小方法的范围

Java继承概念

javascript - AngularJS Controller 继承 : "this" not pointing to the child controller

c++不同类的数组/列表

java - 如何在 java 上显示所有对象

c++ - 将指针从基类型转换为子类型