Let's say there is a CSignals object x. And you define another object y based on x.
Code: Select all
CSignals y=x; // or CSignals y(x);
Here the entire memory block for x is copied or duplicated to define y. This does no harm, if duplicating memory blocks injudiciously is considered “no harm.” While that’s how I have been coding in the past, I also haphazardly used pointers to objects whenever I felt like being mindful of the performance (i.e., speed) and memory uses.
In reality, that approach is not useful because if I make any modifications to py, it alters x as well. Often that’s not what I intended. While the use of
for a function argument prevents it from happening (i.e., protecting the content of the original object), it does not give me a solution if I really need to make some modification of py.
Here comes the idea of ghost copying
. Copy/duplicate the memory block as little and re-use the existing block as much as possible, particularly the actual data buffer which could have a gigantic size. In other words, among member variables below,
Code: Select all
unsigned int nSamples;
unsigned int nGroups;
unsigned char bufBlockSize;
class CSignal : public body
class CTimeSeries : public CSignal
class CSignals : public CTimeSeries
Copying occurs a lot throughout the code, calling one of the following implementations,
Code: Select all
body(const body& src);
body& operator=(const body& rhs);
then a new memory block is allocated replicating the content of buf every time this is called and cleaned when done. What a waste of efforts. So, let’s skip that and just make a ghost copy of rhs with the same buf (to reference the same data buffer), while all other member variables can be defined, used and cleaned as needed within the calling scope. If a CSignals object is created with ghost set true, it doesn’t bother deleting buf (it must not delete buf, because that should be done in the original object destruction).
- This approach is an improvement (i.e., no need/desire to go back to the past). Why?
As an example, consider a fairly long audio variable which we want to play only part of, say from 1 sec time point to 58 sec time point. In the past, just to play the sound, a new CSignals object was created in the local scope and the data content between two time points were copied, then the newly crated CSignals was sent to PlayArray16, where which requires another memory allocation to make a proper memory preparation (e.g., for 8 bit, 16 bit or 32 bit, etc) to send to the sound card. This CSignals object is created and cleaned every time the sound is played. In the new approach, the new CSignals object created locally is ghost, using the original buf as is, but it has different tmark and nSamples, according to the boundary time points. That CSignals object is sent to PlayCSignals, where a new memory allocation (e.g., 16-bit) takes place using existing buf and appropriate indexing. When the new CSignals object goes out of scope, tmark and nSamples, which were used only for the scope, will be cleaned; buf will not, however, and will remain in the lifecycle of the original object.
- This applies also to the following tasks:
- Saving the audio data to a file (.wav, .mp3, .aiff)
- Calculating the rms of the audio (for the whole duration of the screen display or only the selected range)
- Calculating the spectrum from the ghost object of the original