从子类转换到超类到子类? [英] Conversion from subclass to superclass to subclass?

查看:113
本文介绍了从子类转换到超类到子类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的程序需要处理不同类型的笔记: NoteShort NoteLong ...不同种类的注释应以不同方式显示在GUI中。我定义了这些注释的基类,称为 NoteBase

My program needs to handle different kinds of "notes": NoteShort, NoteLong... Different kinds of notes should be displayed in the GUI in different ways. I defined a base class of these notes, called NoteBase.

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

I store these notes in XML; and I have a class which reads from the XML file and store notes' data in vector<NoteBase *> list. Then I found I cannot get their own types, because they are already converted to NoteBase *!

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

Though if(dynamic_cast<NoteLong *>(ptr) != NULL) {...} may works, it's really too ugly. Implementing functions take NoteShort * or NoteLong * as parameter don't work. So, any good way to deal with this problem?

更新:谢谢各位回复。我认为它不应该发生 - 但它确实发生了。我以另一种方式实现它,它现在正在工作。但是,据我记得,我确实在 NoteBase 中声明了(纯)虚函数,但是忘了在派生类的头文件中再次声明它。我想这就是造成这个问题的原因。

UPDATE: Thank you guys for replying. I don't think it should happen neither -- but it did happened. I implemented it in another way, and it's now working. However, as far as I remember, I indeed declared the (pure) virtual function in NoteBase, but forgot to declare it again in headers of the deriving classes. I guess that's what caused the issue.

UPDATE 2(重要):
我从C ++ Primer中找到了这个引用,这可能对其他人有帮助:

UPDATE 2 (IMPORTANT): I found this quotation from C++ Primer, which may be helpful to others:


有时更令人惊讶的是,即使基本指针或$ b,对从$ base转换为派生的
的限制仍然存在$ b引用实际上绑定到派生对象:

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

编译器在编译时无法知道特定的
转换在运行时实际上是安全的。编译器仅在指针或引用的静态类型上查找
,以确定
转换是否合法。在那些我们知道从base到derived的转换
是安全的情况下,我们可以使用static_cast(Section
5.12.4,p.183)来覆盖编译器。或者,我们可以使用
dynamic_cast请求在运行时检查的转换,这将在第18.2.1节(第773页)中介绍。

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).


推荐答案

这里有两个重要的思路和代码,最短的第一个:

There are two significant trains of thought and code here, so shortest first:

您可能不需要重新投票。如果所有注意提供统一的操作(例如 Chime ),那么您可以简单地:

You may not need to cast back up. If all Notes provide a uniform action (say Chime), then you can simply have:

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

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

和每个注意 Chime 使用内部信息(例如,持续时间和音调)。

and each Note will Chime as it should, using internal information (duration and pitch, for example).

这很干净,简单,只需要很少的代码。但它确实意味着所有类型都必须提供并继承自特定的已知接口/类。

This is clean, simple, and requires minimal code. It does mean the types all have to provide and inherit from a particular known interface/class, however.

现在更长当需要知道类型并重新投入其中时,会出现更多涉及的方法。有两种主要方法,一种变体(#2)可以与#3一起使用或组合使用:

Now the longer and far more involved methods occur when you do need to know the type and cast back up to it. There are two major methods, and a variant (#2) which may be used or combined with #3:


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

  1. This can be done in the compiler with RTTI (runtime type information), allowing it to safely dynamic_cast with good knowledge of what is allowed. This only works within a single compiler and perhaps single module (DLL/SO/etc), however. If your compiler supports it and there are no significant downsides of RTTI, it is by far the easiest and takes the least work on your end. It does not, however, allow the type to identify itself (although a typeof function may be available).

这样就完成了:

NewType * obj = dynamic_cast<NewType*>(obj_oldType);


  • 要使其完全独立,请将虚方法添加到基类/接口(用于例如, Uuid GetType()const; )允许对象随时标识自己。这比第三种(真正的COM)方法有一个好处,也是一个缺点:它允许对象的用户做出智能的,或许更快的决定做什么,但需要a)他们演员(可能需要和不安全的 reinterpret_cast 或C风格的演员表)和b)该类型无法进行任何内部转换或检查。

  • To make it entirely independent, adding a virtual method to the base class/interface (for example, Uuid GetType() const;) allows the object to identify itself at any time. This has a benefit over the third (true-to-COM) method, and a disadvantage: it allows the user of the object to make intelligent and perhaps faster decisions on what to do, but requires a) they cast (which may necessitate and unsafe reinterpret_cast or C-style cast) and b) the type cannot do any internal conversion or checking.

    ClassID id = obj->GetType();
    if (id == ID_Note_Long)
        NoteLong * note = (NoteLong*)obj;
        ...
    


  • COM使用的选项是提供一种方法表格 RESULT / * success * / CastTo(const Uuid& type,void ** ppDestination); 。这允许类型a)检查内部铸件的安全性,b)由内部自行决定执行铸造(有关于可以做什么的规则)和c)如果铸造不可能或失败则提供错误。但是,它a)阻止用户表单优化,b)可能需要多次调用才能找到成功的类型。

  • The option which COM uses is to provide a method of the form RESULT /* success */ CastTo(const Uuid & type, void ** ppDestination);. This allows the type to a) check the safety of the cast internally, b) perform the cast internally at its own discretion (there are rules on what can be done) and c) provide an error if the cast is impossible or fails. However, it a) prevents the user form optimizing well and b) may require multiple calls to find a succesful type.

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


  • 以某种方式组合后两种方法(如果00-00-00-0000 Uuid和 nullptr

    Combining the latter two methods in some fashion (if a 00-00-00-0000 Uuid and nullptr destination are passed, fill the Uuid with the type's own Uuid, for example) may be the most optimal method of both identifying and safely converting types. Both the latter methods, and them combined, are compiler and API independent, and may even achieve language-independence with care (as COM does, in qualified manner).

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

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

    The latter two are particularly useful when the type is almost entirely unknown: the source library, compiler, and even language are not known ahead of time, the only available information is that a given interface is provided. Working with such little data and unable to use highly compiler-specific features such as RTTI, requiring the object to provide basic information about itself is necessary. The user can then ask the object to cast itself as needed, and the object is has full discretion as to how that's handled. This is typically used with heavily virtual classes or even interfaces (pure virtual), as that may be all the knowledge the user code may have.

    在你的范围内,这种方法可能对你没用,但可能是有意义的,对于类型如何识别自己并被回归来说当然很重要来自基类或接口。

    This method is probably not useful for you, in your scope, but may be of interest and is certainly important as to how types can identify themselves and be cast back "up" from a base class or interface.

    这篇关于从子类转换到超类到子类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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