Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/reporting-services/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 从子类到超类再到子类的转换?_C++_Inheritance_Polymorphism - Fatal编程技术网

C++ 从子类到超类再到子类的转换?

C++ 从子类到超类再到子类的转换?,c++,inheritance,polymorphism,C++,Inheritance,Polymorphism,我的程序需要处理不同类型的“注释”:NoteShort,NoteLong。。。不同种类的注释应以不同的方式显示在GUI中。我定义了这些注释的基类,称为NoteBase 我将这些注释存储在XML中;我有一个类,它读取XML文件并将注释数据存储在向量列表中。然后我发现我无法获得它们自己的类型,因为它们已经转换为NoteBase* 虽然if(dynamic_cast(ptr)!=NULL){…}可以工作,但它确实太难看了。实现函数将NoteShort*或NoteLong*作为参数不起作用。那么,有什么

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

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

虽然
if(dynamic_cast(ptr)!=NULL){…}
可以工作,但它确实太难看了。实现函数将
NoteShort*
NoteLong*
作为参数不起作用。那么,有什么好办法来解决这个问题吗

更新:谢谢你们的回复。我认为这也不应该发生,但确实发生了。我用另一种方式实现了它,现在它开始工作了。然而,据我记忆所及,我确实在
NoteBase
中声明了(纯)虚函数,但忘了在派生类的头中再次声明它。我想这就是问题的原因

更新2(重要): 我从C++入门中找到了这条引文,这可能对其他人有帮助:

有时更令人惊讶的是,对 即使基指针或 引用实际上绑定到派生对象:

 Bulk_item bulk;
 Item_base *itemP = &bulk;  // ok: dynamic type is Bulk_item
 Bulk_item *bulkP = itemP;  // error: can't convert base to derived
编译器无法在编译时知道特定的 转换在运行时实际上是安全的。编译器只显示 在指针或引用的静态类型上确定 转换是合法的。在这种情况下,当我们知道转换 从基础到派生是安全的,我们可以使用静态_cast(第 5.12.4,第183页)重写编译器。或者,我们可以请求在运行时使用 第18.2.1节(第773页)所述的动态_-cast


这里有两条重要的思路和代码,所以首先:


你可能不需要投回。如果所有
注释
都提供了统一的动作(比如
蜂鸣音
),则您只需:

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

...
for_each(INote * note in m_Notes)
{
    note->Chime();
}
每个
注释
都将使用内部信息(例如,持续时间和音高)按其应该的方式发出
蜂鸣音

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


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

  • 这可以在带有RTTI(运行时类型信息)的编译器中完成,允许它安全地
    dynamic\u cast
    ,并充分了解所允许的内容。然而,这只能在单个编译器中工作,也可能在单个模块(DLL/SO/etc)中工作。如果您的编译器支持RTTI,并且RTTI没有明显的缺点,那么它是迄今为止最简单的,并且在您的终端上花费最少的工作。但是,它不允许类型识别自身(尽管可能有
    typeof
    功能)

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

    NewType * obj = dynamic_cast<NewType*>(obj_oldType);
    
  • 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一样,以限定的方式)

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


    在您的范围内,此方法可能对您没有用处,但可能会引起您的兴趣,并且对于类型如何识别自己以及如何从基类或接口“向上”转换非常重要。

    使用多态性访问每个派生类的不同实现,如下面的示例所示

    class NoteBase
    {
      public:
        virtual std::string read() = 0;
    };
    
    class NoteLong : public NoteBase
    {
      public:
        std::string read() override { return "note long"; }
    };
    
    class NoteShort : public NoteBase
    {
      public:
        std::string read() override { return "note short"; }
    };
    
    int main()
    {
      std::vector< NoteBase* > notes;
      for( int i=0; i<10; ++i )
      {
        if( i%2 )
          notes.push_back(new NoteLong() );
        else
          notes.push_back( new NoteShort() );
      }
    
      std::vector< NoteBase* >::iterator it;
      std::vector< NoteBase* >::iterator end = notes.end();
      for( it=notes.begin(); it != end; ++it )
        std::cout << (*it)->read() << std::endl;
    
      return 0;
    }
    
    类记事本
    {
    公众:
    虚拟std::string read()=0;
    };
    类NoteLong:公共NoteBase
    {
    公众:
    string read()重写{return“note long”;}
    };
    类NoteShort:公共记事本
    {
    公众:
    std::string read()重写{return“note short”;}
    };
    int main()
    {
    标准::向量注释;
    for(int i=0;i::迭代器it;
    std::vector::迭代器end=notes.end();
    for(it=notes.begin();it!=end;++it)
    
    std::cout read()正如其他人所指出的,您应该尝试以一种方式设计基类,使您能够在不强制转换的情况下完成所有需要的工作。如果这不可能(即,如果您需要特定于子类的信息),您可以像以前那样使用强制转换,或者
    ClassID id = ClassID::Null;
    obj->GetAs(id, nullptr);
    if (id == ID_Note_Long)
        NoteLong * note;
        obj->GetAs(ID_Note_Long, &note);
        ...
    
    class NoteBase
    {
      public:
        virtual std::string read() = 0;
    };
    
    class NoteLong : public NoteBase
    {
      public:
        std::string read() override { return "note long"; }
    };
    
    class NoteShort : public NoteBase
    {
      public:
        std::string read() override { return "note short"; }
    };
    
    int main()
    {
      std::vector< NoteBase* > notes;
      for( int i=0; i<10; ++i )
      {
        if( i%2 )
          notes.push_back(new NoteLong() );
        else
          notes.push_back( new NoteShort() );
      }
    
      std::vector< NoteBase* >::iterator it;
      std::vector< NoteBase* >::iterator end = notes.end();
      for( it=notes.begin(); it != end; ++it )
        std::cout << (*it)->read() << std::endl;
    
      return 0;
    }