Using smart pointers and weak pointers is generally the design of last resort in C++. While I'm not saying that this applies to your case but I've lost count of the number of times that teams I've worked in have tied themselves up in knots with object lifetime, tried to solve them with smart pointers and then found themselves tied up in even bigger knots.
I suppose the first question to ask is why do you need some form of smart pointer to manage the interaction between the external representation (the GUI) and the internal representation (your business objects or whatever buzz words fit)? Generally you'd aim to have an object model consisting of your internal representation and then create specific GUI objects as and when they're needed to edit or display the internal objects. In simple cases you can do this editing with automatic objects and not have to worry about object lifetime and memory allocation.
Now this model implies that you've got something like:
- user selects internal object to edit
- application creates external object with a reference to the internal object to let the user edit the internal object
- user uses external object to make their changes
- changes are committed back to the internal object
Validation of data and all that good stuff can be integrated into this melange to taste. The first problem that arises is that in some systems something else can change the internal object while it's being edited. There are several ways to cope with this, including:
- let the user edit the internal object through the external one but discard all their changes at the end and say "ooops." This can actually work quite well when the probability of collision is low.
- everytime the user changes something in the external object ask the internal object whether anything's changed and if it has tell the user. This can work okay but the constant yattering from the external object to the internal object is a pain in the bum to code.
- when something changes in the internal object have it call out to any external objects it's created (using an observer) to tell them what's changed.
In all these methods you don't actually need to manually manage object lifetimes that much. However, if your internal object can be swept into the dustbin of history at any time you're going to have to implement some form of callback to tell the external object it's all gone horribly wrong and abandon everything. Or you have to use something like a reference counted pointer to make sure that the external object keeps the internal object alive to avoid a heavy crash. And then the internal object needs a weak pointer to the GUI so it can find it if it needs to tell it something's changed.
Which is, interestingly, the other way around from the way you described things. So one thing I'd look at doing is thinking about swapping around who owns who - the internal, application/business objects are the things that use the external, UI objects to edit themselves. Two of the last four companies I've worked for have had this problem (one was monitoring hot swappable drives, the app crashed when a drive was swapped and the management dialogue for that drive was active, not a good thing to happen in demos) and it's been fixed by flipping the object ownership on it's head.
Having said all that...
In your implementation it sounds like the relationship between the smart and weak pointer is a bit bent. A weak pointer can't just keep the value of the managed pointer in case the same address is reused, as you've found.
One way you could get around this problem is to add a new level of indirection between the shared/weak pointer and the pointer it's managing. While I've seen this done a variety of ways the simplest way I've seen (although a bit cumbersome) of doing this is to split off the pointer and a couple of reference counts into their own object:
template<typename T>
class locked_ptr
{
public:
locked_ptr( T*ptr ) :
ptr_( ptr ), strong_references_( 1 ), weak_references_( 0 )
{
}
void strong_lock()
{
strong_references_ ++;
}
bool strong_unlock()
{
if( --strong_references_ == 0 )
{
delete ptr_; ptr_ = 0;
}
return return !weak_references_ && !strong_references_;
}
void weak_lock()
{
weak_references_++;
}
bool weak_unlock()
{
weak_references_--;
return !weak_references_ && !strong_references_;
}
private:
T *ptr_;
size_t strong_references_;
size_t weak_references_;
};
(This is just the management stuff, the actual pointer access is left out). Then you can implement your shared pointer and weak pointer in terms of objects of this class. The idea is that the shared and weak pointer keep a pointer to a locked_ptr and delete it when an unlock function returns true. When the pointer is copied one of the lock functions is called. Note that the strong count manages the pointed to object's lifetime and both the strong and weak counts manage the locked_ptr's lifetime.
Anyway, sorry this answer was so long and rambling, hope some of it helps you get back on track.
Cheers,
Ash