SmartPointer: приведение между базовым и производным классами - PullRequest
4 голосов
/ 12 апреля 2011

Скажем, у вас есть такая функция:

SmartPtr<A> doSomething(SmartPtr<A> a);

И такие классы:

class A { }
class B : public A { }

А теперь я делаю это:

SmartPtr<A> foo = new B();
doSomething(foo);

Теперь я хотел бы получить объект SmartPtr<B> из doSomething.

SmartPtr<B> b = doSomething(foo); 

Возможно ли это? Какой тип кастинга мне нужно сделать?
Прямо сейчас я только что нашел то, что считаю безобразным:

B* b = (B*)doSomething().get()

Важные примечания. У меня нет доступа к SmartPtr и doSomething() коду.

Ответы [ 3 ]

8 голосов
/ 12 апреля 2011

Вместо этого вы можете сделать следующее:

B *b = dynamic_cast< B* >( doSomething.get() );

, но вы должны проверить, имеет ли b значение NULL.

0 голосов
/ 12 апреля 2011

Вы можете определить свою собственную функцию шаблона SmartPtrCast, которая выполняет что-то вроде:

template <typename DestT, typename SrcT>
inline SmartPtr<DestT> SmartPtrCast(const SmartPtr<SrcT> &src)
{
    return SmartPtr<DestT>(static_cast<DestT*>(src.get()));
}

Затем, все, что вам нужно сделать изящно из А в Б, это:

SmartPtr<B> b = SmartPtrCast<B>(doSomething(foo));

Предостережение Emptor: Это будет работать только в том случае, если на умный указатель, возвращаемый doSomething(), ссылаются где-то еще, и он не уничтожается, когда выходит из области видимости. Судя по вашему примеру, это так, но это все же не так грациозно, и следует отметить, что два указателя не будут делиться счетчиками ссылок (поэтому, если один из них будет уничтожен, второй потеряет свои данные).

Лучшее решение - отсоединить один из указателей (если в SmartPtr есть метод отсоединения). Еще лучшее решение (если у вас нет метода отсоединения или вы хотите поделиться счетчиком ссылок), это использовать класс-оболочку:

template <typename SrcT, typename DestT>
class CastedSmartPtr
{
private:
    SmartPtr<SrcT> ptr;
public:
    CastedSmartPtr(const SmartPtr<SrcT>& src)
    {
        ptr = src;
    }

    DestT& operator* () const
    {
        return *(static_cast<DestT*> >(ptr.get()));
    }

    DestT* operator->() const
    {
         return static_cast<DestT*> >(ptr.get());
    }

    DestT* get() const
    {
        return static_cast<DestT*> >(ptr.get());
    }
}

template <typename DestT, typename SrcT>
inline SmartPtr<DestT> SmartPtrCast(const SmartPtr<SrcT>& src)
{
    return CastedSmartPtr<SrcT, DestT>(src);
}

Это будет использовать SmartPtr для внутреннего использования (так что счетчик ссылок правильно используется) и static_cast для внутреннего использования до DestT (без влияния на производительность). Если вы хотите использовать dynamic_cast, вы можете сделать это только один раз в конструкторе, чтобы избежать ненужных накладных расходов. Вы также можете добавить в обертку дополнительный метод, такой как конструктор копирования, оператор присваивания, метод отсоединения и т. Д.

0 голосов
/ 12 апреля 2011
SmartPtr<B> b = dynamic_cast<B*>(doSomething().get())

или, возможно, что-то вроде doSomething().dynamic_cast<B*>(), если ваш SmartPtr поддерживает это.

...