Изменение типа объекта в условном - PullRequest
1 голос
/ 29 мая 2010

У меня небольшие проблемы с 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. Есть ли лучший способ?

(Мне не разрешено менять какие-либо классы (Персона, Юрист или Доктор), потому что они являются частью библиотеки, которую имеют и не хотят менять люди, которые будут использовать мой код).

Спасибо

Dave

Ответы [ 5 ]

1 голос
/ 29 мая 2010

Если они не являются полиморфными функциями (как определил их ответ Дэвида), то сделать это будет более чем сложно.

Я бы предложил класс обертки.

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

Я бы рассмотрел использование такого рода решения, только если вы действительно отчаялись.

1 голос
/ 29 мая 2010

Динамическое приведение к подклассу, а затем присвоение результата указателю на суперкласс бесполезно - вы практически вернулись к тому, с чего начали. Вам нужен указатель на подкласс для хранения результата динамического приведения. Кроме того, если конкретный тип вашего объекта 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.

0 голосов
/ 29 мая 2010

Можно ли добавить шим к классам, например так:

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

Таким образом, вам не придется использовать условные выражения. Но это действительно «последнее средство», если вы действительно не можете изменить существующий код библиотеки, и оно требует от ваших пользователей использования Doctor и Lawyer вместо DoctorClass и LawyerClass.

0 голосов
/ 29 мая 2010

Возможно, я здесь полностью упускаю суть или могу неправильно понять ваш пример, поэтому, если я дам мне знать, и я удалю свой пост.

Но не имеет ли больше смысла иметь открытый метод 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; 
} 
0 голосов
/ 29 мая 2010

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() {}.

...