在条件中更改对象的类型

时间:2010-05-28 20:48:01

标签: c++ dynamic-cast

我在使用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;
}

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

  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();
  }

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

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

谢谢,

戴夫

5 个答案:

答案 0 :(得分:1)

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

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

PersonClass* person = // get a Person reference somehow

if(/* person is instance of LawyerClass */)
{
  LawyerClass* lawyer = dynamic_cast<LawyerClass*>(person);
  lawyer->GoToCourt();
}
else
{
  DoctorClass* doctor = dynamic_cast<DoctorClass*>(person);
  doctor->GoToSurgery();
}

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

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();
}

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

答案 1 :(得分:1)

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

我建议使用包装类。

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(); }
};

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

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

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

答案 2 :(得分:0)

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

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

LawyerClass lawyer( person );

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

  PersonClass* person = NULL;
  if(true)
  {
    person = new LawyerClass;
  }
  else
  {
    person = new DoctorClass;
  }

  if ( LawyerClass *lawyer = dynamic_cast< LawyerClass * >( person ) )
  {
    lawyer->GoToCourt();
  }

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

答案 3 :(得分:0)

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

但是有一个名为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)

您是否可以像这样在类中添加垫片:

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;
} 

这样,您就不必使用条件。但如果你真的无法改变现有的库代码,这确实是一个“最后的手段”解决方案,它确实要求你的用户使用DoctorLawyer而不是DoctorClassLawyerClass