2016-10-11 4 views
1

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

Базовый класс:

#define PRINT(s) std::cout << s << std::endl 

class Base { 
public: 
    Base() : m_a(1) {} 
    virtual ~Base() = default; 
    virtual void print() { PRINT("BASE"); } 

    int m_a; 
}; 

Производный класс зависит от шаблона перечисления:

enum eType { e0, e1 }; 

template<eType et> 
class Derived : public Base { }; 

template<> 
class Derived<e0> : public Base { 
public: 
    Derived() { this->m_a = e0; } 
    void print() { PRINT("Derived e0, m_a value: " << e0); } 
}; 

template<> 
class Derived<e1> : public Base { 
public: 
    Derived() { this->m_a = e1; } 
    void print() { PRINT("Derived e1, m_a value: " << e1); } 
}; 

Моя цель состоит в том, чтобы иметь общий указатель на базовый класс, чтобы можно было перейти от 2 производных классов следующим образом:

int main() 
{ 
    std::shared_ptr<Base> sp_00 = std::make_shared<Derived<e0>>(); 

    std::shared_ptr<Base> sp_01 = sp_00; 
    sp_01->print(); 

    std::shared_ptr<Base> sp_10 = std::make_shared<Derived<e1>>(); 

    *sp_01 = *sp_10; 

    sp_01->print(); 
    sp_10->print(); 
} 

Единственная проблема, как на линии *sp_01 = *sp_10; Я ожидаю, что указатель на базовый класс переключится с производного типа Derived<e0> на производный тип Derived<e1>. Однако в моем примере я получаю другой результат для строки sp_01->print(); и строки sp_10->print();, указывающей, что sp_01 остается как Derived<e0>.

Я хочу избежать sp_01 = sp_10;, потому что он изменит указатель. В приведенном выше примере это приведет к sp_00 != sp_01, и я хочу, чтобы оба sp_00 и sp_01 делили тот же объект.

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

class Derived_e0 : public Base { 
public: 
    Derived() { this->m_a = e0; } 
    void print() { PRINT("Derived e0, m_a value: " << e0); } 
}; 
class Derived_e1 : public Base { 
public: 
    Derived() { this->m_a = e1; } 
    void print() { PRINT("Derived e1, m_a value: " << e1); } 
}; 

и следующий код дают тот же результат, что и один с шаблоном.

int main() 
{ 
    std::shared_ptr<Base> sp_00 = std::make_shared<Derived_e0>(); 

    std::shared_ptr<Base> sp_01 = sp_00; 
    sp_01->print(); 

    std::shared_ptr<Base> sp_10 = std::make_shared<Derived_e1>(); 

    *sp_01 = *sp_10; 

    sp_01->print(); 
    sp_10->print(); 
} 

Так что мой вопрос, как переключиться на производный объект, на который указывает общий указатель без изменения самого shared_ptr (который используется в другой части программы?)

Спасибо, если вам нужна Дополнительная информация, пожалуйста, дайте мне знать

+0

Вы не можете изменить «полиморфный тип» 'sp_01' без переназначения' sp_01'. Как вы могли это сделать, если 'Derived ' и 'Derived ' не имели тот же размер? Ваш дизайн (или то, что вы пытаетесь сделать), вероятно, испорчен. – Holt

ответ

1

Вы не можете изменить тип среды выполнения sp_01 без повторного назначения, потому что вы не можете назначить Derived<e1> для Derived<e0> (думать о том, что произойдет, если они не имеют одинаковый размер - вы выделили достаточно размер для Derived<e0>, а не для a Derived<e1>!).

На мой взгляд, ваш дизайн (или то, что вы пытаетесь сделать с ним) где-то испорчен. Тем не менее, если вы действительно хотите сохранить «связь» между sp_00 и sp_01, вы, вероятно, нужен еще один «уровень» указателя:

int main() { 
    std::shared_ptr<Base> *psp_01; 

    std::shared_ptr<Base> sp_00 = std::make_shared<Derived<e0>>(); 
    psp_01 = &sp_00; 

    (*psp_01)->print(); 

    std::shared_ptr<Base> sp_10 = std::make_shared<Derived<e1>>(); 
    psp_01 = &sp_10; 

    (*psp_01)->print(); 
    sp_10->print(); 
} 

Но опять же, я хотел бы проанализировать мой дизайн дважды, прежде чем использовать это.

+0

Спасибо за ваш ответ, он работает с ожидаемым поведением. Я думаю, вы правы в дизайне, и я постараюсь изменить его. Если у меня есть 'sp_00',' sp_01' и другие shared_ptr, которые используют один и тот же класс Derived, возможно ли иметь функцию, которая вызывает все эти экземпляры и повторно назначает их одному и тому же shared_ptr? Это возможно с глобальным вектором, но я думаю, что это плохая оценка. ^^ –

+0

@ SchneiderLoïc Нет, присваивание '* sp_00'' 'sp_01' фактически вызовет' operator = (Base const &) 'of' Base', который переназначает 'Base' часть' sp_01' '' sp_00' (как правило, в вашем случае 'm_a' будет переназначать, но не что-то, что было бы только в' Derived ' или 'Derived '). – Holt

+0

@ SchneiderLoïc См. Http://rextester.com/TJEAV61599, при переназначении '* pxa = * pxb', только' x_' 'X' переустанавливается, атрибут' y_' остается равным '1', хотя 'Y <1>' и 'Y <2>' аналогичны (но они не являются тем же классом!). В этом случае попытка сбрасывать 'pxa' в' Y <2> * 'будет даже сбой. Тип выполнения не изменился с назначением. – Holt

0

Вы можете dynamic_cast необработанный указатель; что-то вроде

Derived* t = dymanic_cast<Derived*>(sp_00.get()) 

И вы получите NULL, если он не может быть брошен, или если он может действительный указатель. Тем не менее, для этого требуется, чтобы RTTI был встроен, что сделает ваш бинарный файл более крупным, и что нужно сделать это вообще - это признак того, что ваш дизайн неправильный.