Not exactly. We give the error because even if the compiler does not need one of the missing definition now, it might need if the code is updated. And since most users do not analyze the code each time they build, we prefer to promote robust code and good practices, even if the code does not yet contain an actual bug.
Scenario 1
I don’t quite understand how the system is supposed to work. The only way for CCPSDataManager
to be destroyed is when the reference count goes to 0, which means that m_ActiveInstance
no longer holds an instance of CCPSDataManager
. Only then the destructor will be called, but there is no need to reset m_ActiveInstance
in this situation, because it is already empty.
So, yes, apparently, just removing the destructor and following the rule of 0 is the way to go.
Scenario 2
Logging
I don’t know your logging needs, so I’m not sure if what I propose is going to be what you need, but here are some thoughts:
- Do you really need those logs? It looks to me more like a debugging tool than like a user feature… (solution 1 of my previous list)
- If you need these low-level logs, I think you probably also need to log when an object is created, including through copy-construction and move-construction, so this means the rule of 5 would be of help
- If you need to log at that level, you usually need to do it for more than one class. So it would be nice to be able to factor this out.
One solution would then be to create a dedicated class for log, and use this class in CInstructionTask
. Something like:
template<class T> // The template can help if you want to count the living instances of different classes
class InstanceLogger {
~InstanceLogger() {
writeLog(LOG_INFORMATION, __FUNCTION__, __LINE__, " cleaning of destructor.");
}
InstanceLogger(InstanceLogger const &) {
writeLog(LOG_INFORMATION, __FUNCTION__, __LINE__, " creation by copy.");
}
// Continue until you have the 5 special member functions
};
Then in you class, you just add a member:
class CInstructionTask {
InstanceLogger<CInstructionTask> logger;
// Destruction and other operations will be logged, without having
// to define a destructor in this class
};
This is solution 2 of my previous list
Cleaning the client library
I’m not sure what this exactly means. I assume that this library must be set-up only once, and cleaned once and only once too. In that case, you should make sure that CInstructionTask
cannot be copied, otherwise, the library may be used while already cleaned, and will end-up being cleaned several times.
You can to that by deleting the copy constructor, move constructor… (solution 3 of the list)
And maybe, depending on the nature of this client library, it can be possible to wrap it in a dedicated class, which would mean that CInstructionTask
can once more be destructor-free (back to solution 2).
Creating these dedicated classes for resource management might seem to a lot of work just to avoid defining some special member functions, but it usually pays off:
- They can usually be re-used across the code
- Since they are focussed on one subject only, they are usually more simple to write, and to test
- Writing a class that manually manages several resources is quite hard, especially for error recovery, but with this strategy, this can no longer happen.