I am developing an application for a data-collection controller. It has to interface with lots of different other devices, which in turn might provide different types and amounts of data. To this end the following hierarchy was devised (it's a bit lengthy but bear with me):
The basic unit of information is the Datum. Datum
itself is an abstract
class, the descendants of which represent different types of [physical]
quantity: temperature, pressure, energy, power, relay state, etc. Each Datum
instance represents a single reading (at some moment in time).
Data are collected by Device
s, which in turn contain several IO
s. A Device
instance represents a concrete physical data-gathering device; its class (a
descendant of the abstract Device
class) represents the model of device and
contains all the model-specific code necessary to interface with it and
extract readings from it. This is done by calling the virtual function void
Device::update()
.
Each IO
instance represents a variable which a device collects. For example,
if the device is a multi-channel temperature monitor, then an IO represents a
single sensor connected to the device. The IO can be queried for a value by
calling IO::get_value()
, which returns a Datum
.
Finally, the Node
class keeps a list of all devices attached to the
controller, another list of all IOs in those devices, and provides methods for
polling all devices at once, individual devices, individual IOs, etc.
These relationships are (a bit loosely) reflected in the following diagram:
Now, for the problem itself:
In all of this, a lot of instances of descendants of abstract classes must be passed around and stored all the time: the Node stores its Devices and their IOs, the devices themselves store their own IOs as well, Data get created and returned and passed around and destroyed, the device and IO list gets updated in place, etc. But it is unclear how to implement all this passing around:
So I am at a loss as to how one might implement a robust system of exchanging objects like this, with more-or-less foolproof ways of ensuring that the objects behave as a variable passed by value (stay in existence as long as they are needed, but not longer) while retaining the duck-typing provided by inheritance of a common interface.
Use std::unique_ptr for unique ownership and std::shared_ptr for shared ownership. This makes passing pointers much safer.
std::unique_ptr cannot be copied, and the pointed-to object is automatically deallocated when the unique_ptr is destroyed (e.g. goes out of scope). Conversely, the std::shared_ptr CAN be copied, and the pointed-to object is only deallocated when all copies of the shared_ptr are destroyed.
If you instrument you code with the above tools and also use std::make_unique (C++14) and std::make_shared (C++11), largely free of manual new
and delete
and avoid a lot of memory related issues.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With