更改条件中的对象类型

时间:2022-01-20 17:04:07

I'm having a bit of trouble with dynamic_casting. I need to determine at runtime the type of an object. Here is a demo:

我在使用dynamic_casting时遇到了一些麻烦。我需要在运行时确定对象的类型。这是一个演示:

#include <iostream>#include <string>class PersonClass{  public:  std::string Name;  virtual void test(){}; //it is annoying that this has to be here...};class LawyerClass : public PersonClass{  public:  void GoToCourt(){};};class DoctorClass : public PersonClass{  public:  void GoToSurgery(){};};int main(int argc, char *argv[]){  PersonClass* person = new PersonClass;  if(true)  {    person = dynamic_cast<LawyerClass*>(person);  }  else  {    person = dynamic_cast<DoctorClass*>(person);  }  person->GoToCourt();  return 0;}

I would like to do the above. The only legal way I found to do it is to define all of the objects before hand:

我想做上面的事情。我发现这样做的唯一合法方法是事先定义所有对象:

  PersonClass* person = new PersonClass;  LawyerClass* lawyer;  DoctorClass* doctor;  if(true)  {    lawyer = dynamic_cast<LawyerClass*>(person);  }  else  {    doctor = dynamic_cast<DoctorClass*>(person);  }  if(true)  {    lawyer->GoToCourt();  }

The main problem with this (besides having to define a bunch of objects that won't be use) is that I have to change the name of the 'person' variable. Is there a better way?

这个的主要问题(除了必须定义一堆不会被使用的对象)是我必须更改'person'变量的名称。有没有更好的办法?

(I am not allowed to change any of the classes (Person, Lawyer, or Doctor) because they are part of a library that people who will use my code have and won't want to change).

(我不允许更改任何课程(人员,律师或医生),因为他们是图书馆的一部分,使用我的代码的人有并且不想改变)。

Thanks,

Dave

5 个解决方案

#1


1  

Dynamic casting to a subclass and then assigning the result to a pointer to superclass is of no use - you practically are back where you started. You do need a pointer to a subclass to store the result of the dynamic cast. Also, if the concrete type of your object is PersonClass, you can't downcast it to a subclass. Dynamic casting can only work for you if you have a pointer to a superclass but you know that the object pointed to is actually an instance of a subclass.

动态转换为子类,然后将结果分配给指向超类的指针是没有用的 - 你几乎回到了你开始的地方。您需要一个指向子类的指针来存储动态强制转换的结果。此外,如果对象的具体类型是PersonClass,则不能将其向下转换为子类。如果你有一个指向超类的指针,但你知道指向的对象实际上是一个子类的实例,那么动态转换只能为你工作。

As others have pointed out too, the best option would be to redesign the class hierarchy to make your methods really polymorphic, thus eliminate the need for downcasting. Since you can't touch those classes, you need the downcast. The typical way to use this would be something like

正如其他人也指出的那样,最好的选择是重新设计类层次结构,使你的方法真正具有多态性,从而消除了向下转换的需要。由于您无法触及这些课程,因此需要向下转发。使用它的典型方法是这样的

PersonClass* person = // get a Person reference somehowif(/* person is instance of LawyerClass */){  LawyerClass* lawyer = dynamic_cast<LawyerClass*>(person);  lawyer->GoToCourt();}else{  DoctorClass* doctor = dynamic_cast<DoctorClass*>(person);  doctor->GoToSurgery();}

Update: if you want to use the subclass instances later, you can do it this way:

更新:如果您想稍后使用子类实例,可以这样做:

PersonClass* person = // get a Person reference somehow...LawyerClass* lawyer = NULL;DoctorClass* doctor = NULL;if(/* person is instance of LawyerClass */){  lawyer = dynamic_cast<LawyerClass*>(person);}else if(/* person is instance of DoctorClass */){  doctor = dynamic_cast<DoctorClass*>(person);}...if(lawyer){  lawyer->GoToCourt();}else if (doctor){  doctor->GoToSurgery();}

Note that this code is more complicated and more error-prone than the previous version. I would definitely try to refactor such code to make it look more like the previous version. YMMV.

请注意,此代码比以前的版本更复杂,更容易出错。我肯定会尝试重构这样的代码,使它看起来更像以前的版本。因人而异。

#2


1  

If they are not polymorphic functions (as David's answer defined them to be), then doing this is going to be more than difficult.

如果它们不是多态函数(正如David的答案所定义的那样),那么这样做将会非常困难。

I would suggest a wrapper class.

我会建议一个包装类。

class PersonWrapper {    PersonClass* person;    virtual void DoWork() = 0;};class DoctorWrapper : public PersonWrapper {    DoctorClass* doc;    virtual void DoWork() { doc->GoToSurgery(); }};class LawyerWrapper : public PersonWrapper {    LawyerClass* lawyer;    virtual void DoWork() { lawyer->GoToCourt(); }};

Of course, this leaves some implementation details to be defined by you, such as assigning the pointer in correct conditions, and is an ugly use of heap. However, it should offer polymorphic functionality, in that you could now do

当然,这会留下一些由您定义的实现细节,例如在正确的条件下分配指针,并且是对堆的丑陋使用。但是,它应该提供多态功能,您现在可以这样做

PersonWrapper* wrap = new LawyerWrapper(new LawyerClass());wrap->DoWork();

I would only consider using this sort of solution if you're genuinely desperate.

如果你真的很绝望,我只会考虑使用这种解决方案。

#3


0  

I may be completely missing the point here or I may be misunderstanding you example so if I am let me know and I will delete my post.

我可能完全忽略了这一点,或者我可能误解了你的例子,所以如果我告诉我,我会删除我的帖子。

But would it not make more sense to have a public method called doJob (or something similar) that calls the virtual method. That way you could do this:

但是有一个名为doJob(或类似的东西)的公共方法调用虚方法会没有意义。那样你就可以这样做:

#include <iostream> #include <string> using namespace std;class PersonClass { public:     std::string Name;     virtual void doWork(){}; //it is annoying that this has to be here... }; class LawyerClass : public PersonClass { public:     void doWork(){GoToCourt();}    void GoToCourt(){cout<<"Going to court..."<<endl;} }; class DoctorClass : public PersonClass { public:     void doWork(){GoToSurgery();}    void GoToSurgery(){cout<<"Doing surgery..."<<endl;}; }; int main(int argc, char *argv[]) {     PersonClass* person;     if(true)     {         person = new LawyerClass();     }     else     {         person = new DoctorClass();     }     person->doWork();     return 0; } 

#4


0  

Is it possible for you to add a shim to the classes like this:

你有可能像这样在类中添加一个垫片:

class Person{public:    virtual void DoJob() = 0;};class Lawyer : public Person, public LawyerClass{ public:    virtual void DoJob()  { GoToCourt(); }}; class Doctor : public Person, public DoctorClass{ public:    virtual void DoJob() { GoToSurgery(); }}; void DoJob(Person& person){    person.DoJob();}int main(int argc, char *argv[]) {    Doctor doctor;    Lawyer lawyer;    DoJob(doctor); // Calls GoToSurgery();    DoJob(lawyer); // Calls GoToCourt();    return 0;} 

This way, you won't have to use a conditional. But this really is a "last-resort" solution if you really can't change the existing library code, and it does require your users to use Doctor and Lawyer instead of DoctorClass and LawyerClass.

这样,您就不必使用条件。但如果您真的无法更改现有的库代码,这确实是一个“最后的手段”解决方案,它确实需要您的用户使用Doctor和Lawyer而不是DoctorClass和LawyerClass。

#5


0  

dynamic_cast allows you to obtain a more precisely-typed reference or pointer to an object of a given type.

dynamic_cast允许您获取更精确类型的引用或指向给定类型的对象的指针。

It does not allow you to change the type of the object. The type of a constructed object cannot change in C++. You must construct a new object.

它不允许您更改对象的类型。构造对象的类型不能在C ++中更改。您必须构造一个新对象。

LawyerClass lawyer( person );

EDIT: to adapt your sample into a rough demo of polymorphism,

编辑:使您的样本适应多态的粗略演示,

  PersonClass* person = NULL;  if(true)  {    person = new LawyerClass;  }  else  {    person = new DoctorClass;  }  if ( LawyerClass *lawyer = dynamic_cast< LawyerClass * >( person ) )  {    lawyer->GoToCourt();  }

Also, you should use an "empty" virtual destructor rather than virtual void test() {}.

此外,您应该使用“空”虚拟析构函数而不是虚拟void test(){}。

#1


1  

Dynamic casting to a subclass and then assigning the result to a pointer to superclass is of no use - you practically are back where you started. You do need a pointer to a subclass to store the result of the dynamic cast. Also, if the concrete type of your object is PersonClass, you can't downcast it to a subclass. Dynamic casting can only work for you if you have a pointer to a superclass but you know that the object pointed to is actually an instance of a subclass.

动态转换为子类,然后将结果分配给指向超类的指针是没有用的 - 你几乎回到了你开始的地方。您需要一个指向子类的指针来存储动态强制转换的结果。此外,如果对象的具体类型是PersonClass,则不能将其向下转换为子类。如果你有一个指向超类的指针,但你知道指向的对象实际上是一个子类的实例,那么动态转换只能为你工作。

As others have pointed out too, the best option would be to redesign the class hierarchy to make your methods really polymorphic, thus eliminate the need for downcasting. Since you can't touch those classes, you need the downcast. The typical way to use this would be something like

正如其他人也指出的那样,最好的选择是重新设计类层次结构,使你的方法真正具有多态性,从而消除了向下转换的需要。由于您无法触及这些课程,因此需要向下转发。使用它的典型方法是这样的

PersonClass* person = // get a Person reference somehowif(/* person is instance of LawyerClass */){  LawyerClass* lawyer = dynamic_cast<LawyerClass*>(person);  lawyer->GoToCourt();}else{  DoctorClass* doctor = dynamic_cast<DoctorClass*>(person);  doctor->GoToSurgery();}

Update: if you want to use the subclass instances later, you can do it this way:

更新:如果您想稍后使用子类实例,可以这样做:

PersonClass* person = // get a Person reference somehow...LawyerClass* lawyer = NULL;DoctorClass* doctor = NULL;if(/* person is instance of LawyerClass */){  lawyer = dynamic_cast<LawyerClass*>(person);}else if(/* person is instance of DoctorClass */){  doctor = dynamic_cast<DoctorClass*>(person);}...if(lawyer){  lawyer->GoToCourt();}else if (doctor){  doctor->GoToSurgery();}

Note that this code is more complicated and more error-prone than the previous version. I would definitely try to refactor such code to make it look more like the previous version. YMMV.

请注意,此代码比以前的版本更复杂,更容易出错。我肯定会尝试重构这样的代码,使它看起来更像以前的版本。因人而异。

#2


1  

If they are not polymorphic functions (as David's answer defined them to be), then doing this is going to be more than difficult.

如果它们不是多态函数(正如David的答案所定义的那样),那么这样做将会非常困难。

I would suggest a wrapper class.

我会建议一个包装类。

class PersonWrapper {    PersonClass* person;    virtual void DoWork() = 0;};class DoctorWrapper : public PersonWrapper {    DoctorClass* doc;    virtual void DoWork() { doc->GoToSurgery(); }};class LawyerWrapper : public PersonWrapper {    LawyerClass* lawyer;    virtual void DoWork() { lawyer->GoToCourt(); }};

Of course, this leaves some implementation details to be defined by you, such as assigning the pointer in correct conditions, and is an ugly use of heap. However, it should offer polymorphic functionality, in that you could now do

当然,这会留下一些由您定义的实现细节,例如在正确的条件下分配指针,并且是对堆的丑陋使用。但是,它应该提供多态功能,您现在可以这样做

PersonWrapper* wrap = new LawyerWrapper(new LawyerClass());wrap->DoWork();

I would only consider using this sort of solution if you're genuinely desperate.

如果你真的很绝望,我只会考虑使用这种解决方案。

#3


0  

I may be completely missing the point here or I may be misunderstanding you example so if I am let me know and I will delete my post.

我可能完全忽略了这一点,或者我可能误解了你的例子,所以如果我告诉我,我会删除我的帖子。

But would it not make more sense to have a public method called doJob (or something similar) that calls the virtual method. That way you could do this:

但是有一个名为doJob(或类似的东西)的公共方法调用虚方法会没有意义。那样你就可以这样做:

#include <iostream> #include <string> using namespace std;class PersonClass { public:     std::string Name;     virtual void doWork(){}; //it is annoying that this has to be here... }; class LawyerClass : public PersonClass { public:     void doWork(){GoToCourt();}    void GoToCourt(){cout<<"Going to court..."<<endl;} }; class DoctorClass : public PersonClass { public:     void doWork(){GoToSurgery();}    void GoToSurgery(){cout<<"Doing surgery..."<<endl;}; }; int main(int argc, char *argv[]) {     PersonClass* person;     if(true)     {         person = new LawyerClass();     }     else     {         person = new DoctorClass();     }     person->doWork();     return 0; } 

#4


0  

Is it possible for you to add a shim to the classes like this:

你有可能像这样在类中添加一个垫片:

class Person{public:    virtual void DoJob() = 0;};class Lawyer : public Person, public LawyerClass{ public:    virtual void DoJob()  { GoToCourt(); }}; class Doctor : public Person, public DoctorClass{ public:    virtual void DoJob() { GoToSurgery(); }}; void DoJob(Person& person){    person.DoJob();}int main(int argc, char *argv[]) {    Doctor doctor;    Lawyer lawyer;    DoJob(doctor); // Calls GoToSurgery();    DoJob(lawyer); // Calls GoToCourt();    return 0;} 

This way, you won't have to use a conditional. But this really is a "last-resort" solution if you really can't change the existing library code, and it does require your users to use Doctor and Lawyer instead of DoctorClass and LawyerClass.

这样,您就不必使用条件。但如果您真的无法更改现有的库代码,这确实是一个“最后的手段”解决方案,它确实需要您的用户使用Doctor和Lawyer而不是DoctorClass和LawyerClass。

#5


0  

dynamic_cast allows you to obtain a more precisely-typed reference or pointer to an object of a given type.

dynamic_cast允许您获取更精确类型的引用或指向给定类型的对象的指针。

It does not allow you to change the type of the object. The type of a constructed object cannot change in C++. You must construct a new object.

它不允许您更改对象的类型。构造对象的类型不能在C ++中更改。您必须构造一个新对象。

LawyerClass lawyer( person );

EDIT: to adapt your sample into a rough demo of polymorphism,

编辑:使您的样本适应多态的粗略演示,

  PersonClass* person = NULL;  if(true)  {    person = new LawyerClass;  }  else  {    person = new DoctorClass;  }  if ( LawyerClass *lawyer = dynamic_cast< LawyerClass * >( person ) )  {    lawyer->GoToCourt();  }

Also, you should use an "empty" virtual destructor rather than virtual void test() {}.

此外,您应该使用“空”虚拟析构函数而不是虚拟void test(){}。