Your class must be self-contained, and avoid unnecessary dependencies. In your example, your "Compiler" class will depend on the CurrentFileName string for its whole existence. Meaning that if CurrentFileName is destroyed before Compiler, you'll have a problem.
Dependents & Managers
So, I guess is depends on the nature of the dependency between the Dependent class and it Manager class (i.e. the Dependent class depends on the Manager class, or, in your example, the Compiler class depends on the std::string class)...
- If the dependency is "soft", then Dependant should make a copy of Manager class.
- If the dependency is "strong", then Dependant could have a reference to the Manager class.
Soft vs. Strong
An example of "soft dependency" is when your Dependant needs only a value, not the exact object itself. A string is usually seen as a value.
An example of "strong dependency" is when your Dependant needs to have access to its Manager, or when rhe Dependent has no meaning without a Manager (i.e. if the Manager is destroyed, then all Dependents should have been destroyed before)
Conclusion
Usually, the dependency is soft.
If in doubt, considers it soft. You'll have less problems (this is one of the way to have a pretty C++ segfault without pointer arithmetics), and still have the possibility of optimize it if needed.
About your case: Soft
Do yourself a favor, and make a copy of the value.
Optimization is the root of all evil, and unless your profiler says the copy of the string is a problem, then, make a copy, as below:
class Compiler
{
public:
Compiler( const std::string& fileName ); // a reference
~Compiler();
//etc
private:
const std::string m_CurrentFileName; // a value
};
Compiler::Compiler(const std::string& fileName )
: m_CurrentFileName(fileName) // copy of the filename
{
}
About my case: Strong
Now, you could be in a situation where the Dependent's existence has no meaning without the Manager itself.
I work currently on code where the user creates Notifier objects to subscribe to events, and retrieve them when needed. The Notifier object is attached to a Manager at construction, and cannot be detached from it.
It is a design choice to impose to the library user the Manager outlives the Notifier. This means the following code:
Manager manager ;
Notifier notifier(manager) ; // manager is passed as reference
The Notifier code is quite similar to the code you proposed:
class Notifier
{
public :
Notifier(Manager & manager) : m_manager(manager) {}
private :
Manager & m_manager ;
} ;
If you look closely at the design, the m_manager is used as an immutable pointer to the Manager object. I use the C++ reference to be sure:
- The reference is defined at construction, and won't ever be changed
- The reference is never supposed to be NULL or invalid
This is part of the contract.