In my C++ project, I have three classes, Particle
, Contact
, and Network
. The Network
class will have N particles (std::vector<Particle> particles
) and Nc contacts (std::vector<Contact> contacts
). Particle
objects will each have a number of contacts, represented by Contact
objects, and contacts are shared by pairs of particles.
It will be necessary for me to know what contacts a specific particle has, and I have decided to be able to obtain that information through the Network
object (e.g., using network.getContacts(Particle particle)
).
My question can be broken into two parts:
First, what would be an efficient and community-recommended way to store the contacts for each particle?
I believe that I can either create a vector of vector of shared pointers to Contact
objects, vector<vector<shared_ptr<Contact>>> prtContacts
(where I have left off std::
for ease of viewing), so that prtContacts[i]
will contain a vector of shared pointers to the contacts of the i
th particle. It has been suggested that using shared_ptr
in this context is useful, as it will ensure that I am not creating duplicate Contact
objects for two particles with a shared contact. Is there an alternative method that would make more sense?
Second, if I do end up creating prtContacts
as defined above, what is the best way to initialize it? My current thinking is to have an initial loop that initializes std::vector<Contact> contacts
with the Nc contacts and then have a second loop that initializes prtContacts
with shared pointers to the Contacts. Is that reasonable or, again, are there alternative, more efficient, approaches?
Thank you!
Some additional information:
The number of particles, N, will range from O(10^1)
to perhaps O(10^4)
. The number of contacts per particle will most likely not exceed 6-7, but will be allowed to vary. Each contact will have a position and contact force (2-component vector) associated with it, and both positions and contact forces will be allowed to vary during the course of simulations (e.g., Monte Carlo simulations).
Best Answer
The right solution will depend a lot on your non-functional requirements. How many particles will you typically have? How many contacts does a particle typically have? How much is a network updated, and in what ways? How fast does it need to be?
I suggest referring to objects by index if you can and then particle contacts can be a list of contact indexes. If a particle typically has a small number of contacts then instead of a
vector
I suggest using something that is optimized for a small number of elements likeboost::container::small_vector
,absl::inlined_vector
,folly::small_vector
orllvm::SmallVector
.Live demo.
Edit: If you have to be able to delete from the middle of the
vector
of contacts then using an index will not work. I would seriously consider if you can change your algorithm so that this is not necessary. Not only does it invalidate pointers, iterators and indexes but it also is quite inefficient because all the contacts after the deletion have to be moved. Perhaps, for example, you can just leave orphaned contacts until the end of the simulation with no connection to a particle. Anyway, if you really need to be able to delete from the middle of the list of contacts perhaps avector
is not the best data structure and you should prefer something like a linked list. The advantage of a linked list is you can do quick deletion from the middle and also pointers to contacts in the list are not invalidated by inserts or deletions so your list of particle contacts can be raw pointers. The downside is it is much slower and does not allow random access.Live demo.
Alternatively, you could use
std::vector<std::unique_ptr<Contact>>
. Raw pointers to the contact won't be invalidated by insertions and deletions. It has the advantage of allowing random access but the disadvantage of being slower at insertions and deletions: Live demoThe right choice will depend on how the data is typically accessed by the simulation.