API design philosophy with smart pointers
I'm thinking about using smart pointers more in my projects and I'm struggling with how to design the public interface when heavily using smart pointers internally.
When you write your classes/libraries, do you use smart pointers wherever you need to store pointers, i.e. wherever you can? Often you need to make internally stored pointers accessible via the public interface of a class or accept pointers from other classes (compare QAbstractItemView::model()/setModel()). Would you then expose e.g. a QWeakPointer to the user? I find this kind of strange, because I've never seen any smart pointers in the public Qt API (in fact, in almost any API).
A pseudo-reallife example (I tried it with class A, B and C, but noticed I myself didn't know anymore what class did what to which, so I'll go for aviation):
Class Airport may hold pointers to instances of class Airplane (weak reference). Airplane instances themselves are stored somewhere as strong references, lets say in class AeroCompany. So the company owns the airplanes, the airport only knows them.
Now it's the user of the library that takes Airplane pointers from AeroCompany and adds it to an Airport, e.g.:
Where Airport::addPlane takes an Airplane* and AeroCompany::plane takes a name and returns the corresponding Airplane*.
Here we have the problem: How can Airport use smart pointers when it only gets raw Airplane* pointers. Is there a magic way to promote raw pointers to weak/strong references while reestablishing the connection with the original strong reference in AeroCompany?
The only possibility I see is that AeroCompany::plane returns and Airport::addPlane takes QWeakPointer<Airplane>. But as I said, it's kind of cluttery and ugly, and I've never seen it in other APIs.
How do you do this in your projects? Restricting smart pointers to only internal mechanisms is kind of silly because smart pointers start getting beneficial only when you share references across many classes...right?
Thanks for your thoughts,
(Hope this isn't forbidden)
Why do you insist on using smart pointers - my initial response was kind of hasty and I didn't really paid much attention to the situation you explain. IF only companies own airplanes, then why do you need smart pointers for shared ownership? The idea of smart pointers is lifetime management, and in the case of reference counting, it is shared ownership, so that the object is alive for as long as it is referenced and the last owner to go out deletes it. But in you case there is only one owner, the company, airports shouldn't delete planes right?
But if they're only for lifetime management, what's QWeakPointer for? My main goal for using them would be to add a layer of robustness against dangling pointer bugs and such. And of course decouple components. Right now, every time company wants to delete an airplane, it has to notify all airports that the pointer to the airplane they hold will become invalid (or the airplane has to remember which other objects than company have a pointer to it and notify them – this is information which just bloats airplanes unnecessary and couples them with everything that ever might want to hold airplanes). While when using a QWeakPointer, the airport will know automatically that that airplane was deleted somewhere else, and at least not segfault with a dangling pointer but actually output a debug message and try to recover gracefully without taking down the whole application.
I see, in that case this is the proper line of action, I haven't been in such scenario. My approach would be to store airports the airplanes are in inside the plane instance and in the plane destructor go through the airports and remove the plane. That would increase the size of a plane, but by how much? In how many airports can a single plane be at the same time?
At any rate, I'd refer planes cleaning themselves up from the airports once deleted instead of checking every time whether a plane has been deleted.
bq. How can Airport use smart pointers when it only gets raw Airplane* pointers. Is there a magic way to promote raw pointers to weak/strong references while reestablishing the connection with the original strong reference in AeroCompany?
Keep to smart pointers, write a cast operator that casts smart pointer to raw for the constructor that takes a raw pointer, in the body of the cast operator you can do all the extra stuff raw pointer cannot.