2015-07-14 3 views
4

У меня есть экземпляр базового класса, есть производный класс, который наследуется от базового класса, я хочу преобразовать базовый экземпляр в производный экземпляр (если возможно, без копирования чего-либо (возможно, отправки к производному классу ссылка базового класса)), как я могу это достичь?Создание экземпляра производного класса с использованием экземпляра базового класса

Примечание: Мне нужно это, потому что я использую шаблон фабричного проектирования, который идентифицирует производный класс, который необходимо создать, используя параметр, расположенный в базовом экземпляре.

//class A 
//class B: public A (pure virtual) 
//class C: public B 

B BFactory::makeB(A &a) { 
    int n=a.getN(); 
    if(n==1){ 
     return new C(); 
    } 
} 

Спасибо.

+1

Вы не можете изменить тип экземпляра. Лучшее, что вы можете сделать, это заменить экземпляр одним из типов производного класса. Можете ли вы разместить код вокруг того места, где вам нужно это сделать? Может быть, мы покажем вам, как сделать замену. –

+0

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

+0

Вы вызывающе должны размещать код вокруг места, которое вам нужно для этого. –

ответ

4

Рассмотрим случай автомобиля.

Вы можете рассматривать Lamborghini как автомобиль.

Вы можете рассматривать Yugo как автомобиль.

Вы можете рассматривать автомобиль как Lamborghini, если это Lamborghini. В C++ это означает указатель на автомобиль, который действительно указывает на Lamborghini. Чтобы вернуть указатель Lamborghini из указателя автомобиля, вы должны использовать dynamic_cast. Если автомобиль не указывает на Lamborghini, dynamic_cast вернет NULL. Это мешает вам попытаться сбить Yugo как Lamborghini и выдуть двигатель Yugo.

Но когда Lamborghini обрабатывают как автомобиль, он может делать только автомобильные вещи. Если вы скопируете Lamborghini в машину, вы навсегда потеряете всю Lamborghini-ness. Он исчез.

Код времени!

Это, я боюсь, что не может быть сделано:

//class A 
//class B: public A (pure virtual) 
//class C: public B 

B BFactory::makeB(A &a) { 
    int n=a.getN(); 
    if(n==1){ 
     return new C(); 
    } 
} 

C копируется в B и B возвращается. B понадобится конструктор, который взял C, но точка спорная. B не может быть создан, если он является виртуальным. Теперь мы будем игнорировать утечку, которая была бы new C()

Также не может использовать ссылку на эту работу, в значительной степени та же проблема, так что вы в ловушке, возвращающая указатель

B * BFactory::makeB(A &a) { 
    int n=a.getN(); 
    if(n==1){ 
     return new C(); 
    } 
} 

сейчас Я собираюсь сделать предложение: построить функцию грим в B и обрабатывать случай, когда а не отображает ни к чему признанным В.

class B: public A 
{ 
public: 
    virtual ~B(){} 
    static B * makeB(A & a) 
    { 
     switch(a.getN()) 
     { 
      case 1: 
       return new C(); 
     } 
     return NULL; 
    } 
}; 

Но это приводит к другой рекомендации: Почему B ничего знать ? И в чем смысл A на этом уровне? Почему A хранит коды сборки для классов два или более шагов вниз по иерархии? Плохое с точки зрения обслуживания. Точка объектов - они знают, кто они и как манипулировать собой. Короткое замыкание приводит к боли.

class B: public A 
{ 
public: 
    virtual ~B(){} 
    virtual B* makeB() = 0; 
}; 

Теперь B только делает Bs, не нуждается в помощи от А, и те, кто простираться B застряли выяснить, как сделать себя - задача, они должны знать лучше, чем кто-либо другой. Гораздо безопаснее, потому что никогда не существует никакой возможности для кода, непризнанного B для нового класса.

class C: public B 
{ 
public: 
    B* makeB() 
    { 
     return new C(); 
    } 
}; 

class D: public B 
{ 
public: 
    B* makeB() 
    { 
     return new D(); 
    } 
}; 

Edit: Традиционный завод

Вы просите абстрактную фабрику. Для этого вам ничего не нужно. Вам даже не нужен класс. Вы, конечно, не нуждаетесь в классе A. Цель такого типа фабрики заключается в том, что вызывающий объект ничего не знает о классе. Предоставляя А, вызывающий абонент должен знать, как сделать А или есть еще один завод, который делает A.

Сначала немного настройки в файле заголовка BFactory.h:

#ifndef BFACTORY_H_ 
#define BFACTORY_H_ 

#include <exception> 
class B 
{ 
public: 
    virtual ~B(){} 
    virtual std::string whatAmI() = 0; 
protected: 
    // data members common to all B subclasses 
}; 

enum bType 
{ 
    gimmie_a_C, 
    gimmie_a_D, 
    gimmie_an_E 
}; 

class BadTypeException: public std::exception 
{ 
public: 
    const char* what() const noexcept 
    { 
     return "Dude! WTF?!?"; 
    } 
}; 

B* BFactory(enum bType type); 

#endif /* BFACTORY_H_ */ 

Здесь я немного отклонился от книги. Вместо того, чтобы использовать целое число для идентификации типа, который будет создан, я собираюсь использовать перечисление. Две причины: проще читать и понимать gimme_a_C, чем 1, и генерирует ошибку компилятора, если вы попытаетесь предоставить значение, которое не перечислили.

enum bType 
{ 
    gimmie_a_C, 
    gimmie_a_D, 
    gimmie_an_E 
}; 

И исключение флага глупости, если перечисление обновляется с новыми типами (gimmie_an_E), но на заводе нет.

class BadTypeException: public std::exception 
{ 
public: 
    const char* what() const noexcept 
    { 
     return "Dude! WTF?!?"; 
    } 
}; 

Это все, что нужно клиенту завода. Они не видят C. Они не видят D. Они не имеют понятия, что C и D существуют каким-либо образом, кроме имен, перечисленных в enum bType. Все, что они когда-либо видеть это указатели на B.

Теперь для реализации BFactory.cpp:

#include "BFactory.h" 

class C:public B 
{ 
    std::string whatAmI() 
    { 
     return "C"; 
    } 
}; 

class D:public B 
{ 
    std::string whatAmI() 
    { 
     return "D"; 
    } 
}; 

B* BFactory(enum bType type) 
{ 
    switch(type) 
    { 
     case gimmie_a_C: 
      return new C(); 
     case gimmie_a_D: 
      return new C(); 
     default: 
      throw BadTypeException(); 
    } 
} 

Я оставлю это до читателя, чтобы обнаружить глупую ошибку в коде выше, что делает эти ошибки и почему я их не люблю.

И использование, main.cpp:

#include "BFactory.h" 

int main() 
{ 
    B * temp; 
    temp = BFactory(gimmie_a_C); 
    std::cout << temp->whatAmI() << std::endl; 
    delete temp; 
    temp = BFactory(gimmie_a_D); 
    std::cout << temp->whatAmI() << std::endl; 
    delete temp; 
    //temp = BFactory(1001); // won't compile 
    try 
    { 
     temp = BFactory(gimmie_an_E); // will compile, throws exception 
     std::cout << temp->whatAmI() << std::endl; 
    } 
    catch(BadTypeException& wtf) 
    { 
     std::cerr << wtf.what() << std::endl; 
    } 
} 

Там до сих пор абсолютно никакой пользы для или участие А. А, если он существует, должен вообще ничего о B или дети B.

В наши дни есть небольшое улучшение, которое мы можем сделать, чтобы указатели были немного безопаснее. unique_ptr позволяет нам сохранить полиморфные преимущества указателя на B без проблем управления памятью.

std::unique_ptr<B> BFactory(enum bType type) 
{ 
    switch(type) 
    { 
     case gimmie_a_C: 
      return std::unique_ptr<B>(new C()); 
     case gimmie_a_D: 
      return std::unique_ptr<B>(new D()); 
     default: 
      throw BadTypeException(); 
    } 
} 

и новый главный:

int main() 
{ 
    std::unique_ptr<B> temp; 
    temp = BFactory(gimmie_a_C); 
    std::cout << temp->whatAmI() << std::endl; 
    temp = BFactory(gimmie_a_D); 
    std::cout << temp->whatAmI() << std::endl; 
} 
+0

Благодарим за полезный ответ. Однако вы проигнорировали ту часть, где нужно определить, какой класс следует построить 'if (a.getN() == 1)' – Neet33

+0

И затем создание экземпляра 'C' или' D' (в соответствии с на 'a.getN()'), который наследует в конечном итоге от 'a' (имеет поля данных' a') – Neet33

+0

@ neet33 B является подклассом A, поэтому B и любой подкласс B также имеют поля данных A.Если вы собираетесь построить B на основе B, он уже знает, какой тип он должен быть, поэтому A не нужно участвовать. Создание B на основе A будет подвержено ошибкам, например, как вы будете обрабатывать 'makeB', когда он вызывается с помощью' class Q: public A'? Q - это A, но не B и, вероятно, не создаст хорошую основу для C или D. Это будет похоже на то, что вытащите автомобиль из схемы поезда. Бедный автомобиль будет постоянно удивляться: «Чувак, где мои следы?» – user4581301

0

Хотя это невозможно изменить тип объекта, вы можете создавать экземпляры базовых и производных классов одни и те же данные:

 #include <memory> 
     #include <iostream> 

     class Base 
     { 
     protected: 

      struct CommonData 
      { 
       int A; 
       int B; 
      }; 

      std::shared_ptr<CommonData> m_data; 



     public: 

      Base() : m_data(std::make_shared<CommonData>()) 
      { 
       m_data->A = 0; 
       m_data->B = 0; 
      } 

      void SetData(Base * source) 
      { 
       m_data = source->m_data; 
      } 


      int A() const { return m_data->A; } 
      int B() const { return m_data->B; } 

      void SetA(int value) { m_data->A = value; } 
      void SetB(int value) { m_data->B = value; } 
     }; 

     class Derived : public Base 
     { 
     public: 
      int C; 
     }; 

     using namespace std; 

     int _tmain(int argc, _TCHAR* argv[]) 
     { 

      Base base; 
      base.SetA(12); 
      base.SetB(46); 

      Derived derived; 
      derived.SetData(&base); 
      derived.C = 555; 

      cout << derived.A() << endl; // 12   
      cout << derived.C << endl; // 555; 

      cin.get(); 
     } 
Смежные вопросы