[updated][c++] this + shared_ptr как?
А enable_shared_from_this из буста не то?
и так для каждого класса и для каждого оператора new.А перегрузить оператор new?
Ну или как вариант специализировать shared_ptr<Test>.
унаследуйся от
фактически у меня только std::shared_ptr есть, ну и weak
похоже это как раз то, но так как я без буста, попробую покрасть идею.
ты на каком компиляторе? если есть shared_ptr, то должен быть и enable_shared_from_this
Using built-in specs.
Target: i686-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.4-14ubuntu5.1' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=i686-linux-gnu --host=i686-linux-gnu --target=i686-linux-gnu
Thread model: posix
gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5.1)
о, да! алилуйя, оно там есть!
gcc 4.4загляни в исходники, неужели там нет того, что надо?
вот тут пишут, что в 4.4.1 уже есть: http://stackoverflow.com/questions/2571850/why-does-enable-s...
о, да!поздравляю, Шарик, ты балбес (с)
делаю вот так, и это работает:
#include <iostream>
#include <tr1/memory>
class Root;
class Child
{
public:
Child(std::tr1::shared_ptr<Root> owner_p) : owner(owner_p)
{
}
~Child()
{
std::cout << "Child::~Child()" << std::endl;
}
void test();
private:
std::tr1::shared_ptr<Root> owner;
};
class Root : public std::tr1::enable_shared_from_this<Root>
{
public:
Root()
{
}
void init()
{
child = std::tr1::shared_ptr<Child>(new Child(shared_from_this()));
}
void say()
{
std::cout << "I'm a root" << std::flush;
}
void test()
{
child->test();
}
~Root()
{
std::cout << "Root::~Root()" << std::endl;
}
private:
std::tr1::shared_ptr<Child> child;
};
void Child::test()
{
std::cout << "I'm a child, my owner says: ";
owner->say();
std::cout << std::endl;
}
int main(int , char *[])
{
std::tr1::shared_ptr<Root> r(new Root());
r->init();
r->test();
return 0;
}
а когда делаю так, как на мой взгляд удобнее и естественнее, то получаю terminate called after throwing an instance of 'std::bad_weak_ptr'
what(): tr1::bad_weak_ptr
#include <iostream>
#include <tr1/memory>
class Root;
class Child
{
public:
Child(std::tr1::shared_ptr<Root> owner_p) : owner(owner_p)
{
}
~Child()
{
std::cout << "Child::~Child()" << std::endl;
}
void test();
private:
std::tr1::shared_ptr<Root> owner;
};
class Root : public std::tr1::enable_shared_from_this<Root>
{
public:
Root()
{
child = std::tr1::shared_ptr<Child>(new Child(shared_from_this()));
}
void say()
{
std::cout << "I'm a root" << std::flush;
}
void test()
{
child->test();
}
~Root()
{
std::cout << "Root::~Root()" << std::endl;
}
private:
std::tr1::shared_ptr<Child> child;
};
void Child::test()
{
std::cout << "I'm a child, my owner says: ";
owner->say();
std::cout << std::endl;
}
int main(int , char *[])
{
std::tr1::shared_ptr<Root> r(new Root());
r->test();
return 0;
}
я даже догадываюсь почему, но как правильно использовать не пойму.
template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
{
boost::detail::sp_enable_shared_from_this( this, p, p );
}
template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )
{
if( pe != 0 )
{
pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
}
}
template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
{
if( weak_this_.expired() )
{
weak_this_ = shared_ptr<T>( *ppx, py );
}
}
shared_ptr<T> shared_from_this()
{
shared_ptr<T> p( weak_this_ );
BOOST_ASSERT( p.get() == this );
return p;
}
#include <iostream>
#include <tr1/memory>
class Root;
class Child
{
public:
Child(std::tr1::shared_ptr<Root> owner_p) : owner(owner_p)
{
}
~Child()
{
std::cout << "Child::~Child()" << std::endl;
}
void test();
private:
std::tr1::shared_ptr<Root> owner;
};
class Root : public std::tr1::enable_shared_from_this<Root>
{
public:
Root()
{
std::tr1::shared_ptr<Root> sharedThis(this);
child = std::tr1::shared_ptr<Child>(new Child(sharedThis));
}
void say()
{
std::cout << "I'm a root" << std::flush;
}
void test()
{
child->test();
}
~Root()
{
std::cout << "Root::~Root()" << std::endl;
}
private:
std::tr1::shared_ptr<Child> child;
};
void Child::test()
{
std::cout << "I'm a child, my owner says: ";
owner->say();
std::cout << std::endl;
}
int main(int , char *[])
{
std::tr1::shared_ptr<Root> r(new Root());
r->test();
return 0;
}
Шареды и вики, указывающие на один и тот же объект и созданные через всякие присваивания и копи-конструкторы (как по-идее и должны плодиться), шарят внутри себя кишки, в которых всякие счетчики и указатель на хранящийся объект. У тебя же здесь два разных шареда, один в конструкторе и один в майне, которые каждый со своими кишками, но содержат один и тот же указатель на объект. Когда выполняется ретурт из мейна, у тебя вызывается деструктор шареда в майне, который вызывает деструктор шареда чайлда, который вызывает деструктор шареда рута в чайлде. В итоге эта вся цепочка удаляет два рута и один чайлд. В общем, косяк тут в том, что ты два независимых шареда создал и пропихнул в них один и тот же объект.
сделал вот такэти смарт пойнтеры для тебя слишком смарт
1) у тебя проблема с дизайне: циклические зависимости между сущностями. постарайся переработатьработает
2) если с переработкой туго, то надо четко понимать время жизни каждого объекта и кто кем владеет
в частности: если A ссылается на B, а B ссылается на A, то лишь одна из этих ссылок должна быть сильной (shared_ptr), а вторую надо сделать слабой (weak_ptr), иначе будут утечки памяти
3) проработай последовательность создания и уничтожения сущностей
один из возможных и логичных сценариев (метакод):
RootPtr root = CreateRoot();
ChildPtr child = CreateChild();
root->AddChild(child);
//implementation
void Root::AddChild(ChildPtr child)
{
m_child = child;
child->SetRoot(enable_shared_from_this());
}
void Child::SetRoot(RootPtr root)
{
//store week pointer on root
m_RootWeek = root;
}
успехов
m_child = child;
child->SetRoot(enable_shared_from_this());
вот именно этого я хотел избежать - было очень удобно, когда все что можно создавалось в конструкторе. Очень не хотелось вводить что-то вроде virtual afterConstruction().
Про weak я так понял у меня ссылка вверх на owner должна быть weak.
Вообще думалось ввести некий smart_pointer который является корнем наследования для 2 или более поинтеров, которые соответственно внутри содержат либо weak либо shared, оба ведут себя как указатели, а в деструкторе делают соответствующее вложенному указателю действие. Чтобы можно было внутрь объекта отдавать любой смарт поинтер по полиморфизму (по ссылки или по указателю) и он мог им пользоваться независимо от его "сильности", а решать какой именно поинтер я отдам я мог каждый раз в месте передачи, а не жестко кодировать в сигнатуре функции через которую передаю. Однако это выглядит слишком сложно и пугает возможностью запутать самого себя (и коллегу, который будет это использовать) и получить вдобавок оверхед на порядок больше чем при работе даже с std::shared_ptr.
я догадываюсь. я думал внутренний weak выцепит кишки из первого использованного шареда и назначит их второму при присваивании. оказалось что это не так.
эстетику портит звёздочка, которая появилась перед new.
#include <iostream>
#include <tr1/memory>
class Root;
class Child
{
public:
Child(std::tr1::weak_ptr<Root> owner_p) : owner(owner_p)
{
}
~Child()
{
std::cout << "Child::~Child()" << std::endl;
}
void test();
private:
std::tr1::weak_ptr<Root> owner;
};
class Root : public std::tr1::enable_shared_from_this<Root>
{
public:
Root()
{
std::tr1::shared_ptr<Root> sharedThis(this);
child = std::tr1::shared_ptr<Child>(new Child(sharedThis));
tmpThis = sharedThis;
}
void say()
{
std::cout << "I'm a root" << std::flush;
}
operator std::tr1::shared_ptr<Root>()
{
std::tr1::shared_ptr<Root> result = shared_from_this();
tmpThis.reset();
return result;
}
void test()
{
child->test();
}
~Root()
{
std::cout << "Root::~Root()" << std::endl;
}
private:
std::tr1::shared_ptr<Child> child;
std::tr1::shared_ptr<Root> tmpThis;
};
void Child::test()
{
std::cout << "I'm a child, my owner says: ";
owner.lock()->say();
std::cout << std::endl;
}
int main(int , char *[])
{
std::tr1::shared_ptr<Root> r( * new Root());
r->test();
return 0;
}
придумал
Оставить комментарий
elenangel
Как использовать this внутри класса, если снаружи этот класс принадлежит кому-то по shared_ptr, но не известно кому?Использовать вместе raw pointer и shared нехорошо. Завернуть где надо shared_ptr<ThisClass>(this) тоже нельзя - его тогда 2 раза грохнут.
Придумал пока в классе хранить weak_ptr на него самого, но его надо задавать после конструктора когда уже известен указатель и сохранен снаружи в какой-то shared.
То есть придется делать что-то вроде
и так для каждого класса и для каждого оператора new.
можно завернуть в макрос или затемплейтить, конечно, но нет ли способа лучше?