What would originally be a simple bulk deallocation now requires calling ~2 destructors per entry (one from the main object, one from shared_ptr).
But what was the actual cost of the main object constructor? All it has to do is call the shared_ptr destructor. Did the compiler not optimize it away enough?
And by bulk deallocation, how would that have been possible if you didn't use manual deallocation for each object in turn anyway? Would you have implemented the struct with the data embedded in the object, without an extra indirection? Or maybe put that data in its own vector/array, as is common in graphics/games programming? If so, they should have done that whether or not there was shared_ptr. It sounded like more thought needed to be given to the data design, rather than the fault of managed memory.
shared_ptr also requires a thread-safe reference count, which is a slowdown in itself.
Assuming an inherently multithreaded process, if you are sharing ownership of an object amongst threads, you would want reference counts to be atomic. If ownership is not shared - you shouldn't be using a shared_ptr.
I'm certain you can avoid most of the slowdown with shared_ptr if you already know what can happen, but there's still a developer who uses it without knowing what will happen ahead of time.
It sounds like the problems started out with an ignorance of data design and the purpose of shared_ptrs (ie, declare shared ownership rather than a catch all for Java-like allocation of objects).
I tried using some of the "safe pointer" stuff - depending on which one is chosen, there is a performance hit depending on which safety mechanism you choose. For example, the auto-deallocate safety net slows things down a lot, while bounds checking tends to be less of a problem.
With shared_ptr, the performance hit is with the reference count updating, so you should still pass a shared_ptr as a reference when possible.
With unique_ptr and the std containers (of movable types), I can't imagine the auto-deallocation would cost any more than manual deallocation in C since they are really just the equivalent of one manual deallocation call.
A triangle which has an angle of 135 degrees is called an obscene triangle.