1

我在使用 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、Lawyer 或 Doctor),因为它们是将使用我的代码的人拥有并且不想更改的库的一部分)。

谢谢,

戴夫

4

5 回答 5

1

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

我会建议一个包装类。

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

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

于 2010-05-28T21:19:57.490 回答
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。

于 2010-05-28T21:00:21.067 回答
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() {}.

于 2010-05-28T20:56:01.640 回答
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; 
} 
于 2010-05-28T20:58:16.907 回答
0

您是否可以向这样的类添加 shim:

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

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

于 2010-05-28T21:05:43.097 回答