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, ¬e))
...
以某种方式组合后两种方法(例如,如果传递了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, ¬e);
...
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;
}