[cpp] Присваивание умных указателей

karkar

Пусть есть шаблон умного указателя CSmartPtr<T> с подсчетом ссылок.
Пусть есть тип T1 и наследный от него T2. Есть два умных указателя CSmartPtr<T1> p1 и CSmartPtr<T2> p2. Как описать оператор присваивания, чтобы делать p1 = p2?
Проблема в том, что если делать
CSmartPtr& operator=(const CSmartPtr<T> &sp)
то он не вызывается - ведь справа все-таки другой тип.
А если
CSmartPtr& operator=(T* p)
то срабатывает (т.к. есть оператор T*) но теряется информация о счетчике ссылок.

katrin2201

Возможно скажу глупость, поэтому ногами не пинать.
Шаблон нельзя унаследовать от другого класса?
Скажем, есть CSmartPtrVoid, и шаблон class CSmartPtr<T> : public CSmartPtrVoid

bobby

Можно-можно

ppplva

Можно рядом с определением T2 описать присваивание указателей, внешней функцией.
Или в указателе описать присваивание шаблоном по двум типам - но это сработает для любой пары типов.

kokoc88

Совершенно не ясно, как умный указатель на один тип данных должен будет хранить другой тип данных и как это всё преобразовывать?! Оператор можно написать так, хотя это очередной хак...
template <class T> class CTest
{
public:
CTest(T t)
{
m_T = t;
}
T m_T;
template <class T2> CTest<T>& operator=(CTest<T2> src)
{
std::cout << "inside operator=" << std::endl;
m_T = src.m_T;
return *this;
}
};

ppplva

Ну да, все правильно. А в m_T = src.m_T действует обычный плюсовый контроль типов + dynamic_cast.

karkar

Унаследовать можно, только не совсем понятно, что дальше с этим делать.
CSmartPtrVoid не соддержит информации о типе данных (Т как мне его использовать в присваивании?
Другие варианты, как я понимаю, требуют в явном виде указать какие у Т1 наследники есть. Это сделать можно, но не хотелось бы..

kokoc88

Не понял? Если ты про то, что я написал, то оно вполне поддерживает запись вида
CTest<int> t1;
CTest<double> t2;
t1 = t2;
Ну или для твоего варианта:
class A{ };
class B: public A { };
.....
A a;
CTest<A> t1(a);
B b;
CTest<B> t2(b);
t1=t2;
(t2 = t1; писать нельзя)

karkar

Да, верно, спасибо!
Еще тупой вопрос: можно ли избежать необходимости делать m_T public?

kokoc88

Можно сделать setter/getter:
T& GetT { return m_T; }
void SetT(T t) { m_T = t; }
...
SetTT)src.GetT;
Но тогда при вызове SetT будет создаваться новый экземпляр класса. Этого можно избежать если писать SetT(T& t но будет работать только для родственных классов.
Ещё можно сделать через указатели:
void SetT(T* t) { m_T = *t; }
T* GetT { return &m_T; }
...
SetTT*)src.GetT;

rosali

Просто посмотри исходники std::auto_ptr или boost::shared_ptr. А то тебе сейчас тут навпаривают...

karkar

Посмотрел и то, и другое, и третье (CComPtr в ATL).
У auto_ptr логика работы другая - destructive copy, мне не подходит. А в shared_ptr очень похоже на то, что предложил , но требуется компилятор получше моего VC6. Для VC6 там очень простая схема, где мой вопрос остается без внимания.
Всем спасибо!

kokoc88

ИМХО, необходимость таких извращений зачастую означает неправильный подход к решению поставленной задачи.

karkar

Каких именно извращений? Умных указателей или наследования?

Ситуация очень проста: был проект с некоторой иерархией классов, где никаких умных указателей не было. Захотелось их туда прикрутить, тут-то и возникли проблемы. Конкретный вопрос с присваиванием возник, когда надо было хранить в одном контейнере указатели на разные родственные объекты.

kokoc88

И стандартные смарт поинтеры тебя не устраивают чем?..

karkar

Уже описал выше.

kokoc88

Что ты описал выше?... Оно подходит для auto_ptr:
std::auto_ptr<B> pB(new B;
std::auto_ptr<A> pA;
pA = pB;

margadon

у этого пойнтера поведение отлично от того, что требуется

karkar

Насколько я понимаю, после pA = pB; pB уже не будет ссылаться на нужный объект. Это не то, что мне надо.

rosali


Проблема в том, что если делать
CSmartPtr& operator=(const CSmartPtr<T> &sp)
то он не вызывается - ведь справа все-таки другой тип.
 
template<class S>
CSmartPtr& operator=(const CSmartPtr<S> &sp) {
this->_p = sp._p; //compiles only if S extends T
//increment ref count...
}

karkar

Спасибо. Именно это предложил , и именно так я уже и сделал. Работает.
Оставить комментарий
Имя или ник:
Комментарий: