views:

186

answers:

8

It's considered a bad idea/bad design, have a class with a constructor accepting a reference, like the following?

class Compiler
{
public:
  Compiler( const std::string& fileName );
  ~Compiler();
//etc
private: 
  const std::string& m_CurrentFileName;
};

or should I use values? I actually do care about performance.

Thank you in advance for the answers.

A: 

If you are writing a compiler, copying the filename once or twice will not be the bottleneck. This is more of a C++ style issue, which I leave to the more C++ savvy people around here.

JesperE
+1  A: 

If you can guarantee that the string that the reference uses won't go out of scope until the class does, then it is maybe Ok to use (I wouldn't). If you were having issues, you may be better passing the string around with a reference counted smart pointer.

It may be worth, and safer, writing your application so that the class constructor copies the string, then when you have performance issues, profile them. Most of the time it is not this sort of thing that causes the issues, but at the algorithm and data structure level.

Yacoby
+14  A: 

If you used a value parameter in this case, you would have a reference in the class to a temporary, which would become invalid at some point in the future.

The bad idea here is probably storing a reference as a member in the class. It is almost always simpler and more correct to store a value. And in that case, passing the constructor a const reference is the right thing to do.

And as for performance, you should only care about this where it matters, which you can only find out by profiling your code. You should always write your code firstly for correctness, secondly for clarity and lastly for performance.

anon
+1 for the paragraph about performance.
JesperE
Correctness comes first and then performance! +1
aJ
Whilst I agree with you regarding performance it's also useful to adopt "sensible defaults"; and passing a const reference is one of those defaults. It's not going to do any harm and it MAY perform better. As you say, the problem here is storing a reference rather than a value and not how that a reference is passed into the ctor. Personally I don't view this kind of thing as a performance optimisation just the 'best way to do it'. I include things like precalculating loop end conditions in this thinking as well `for (X::const_iterator it = x.begin(), end = x.end(); it != end; ++it)` etc...
Len Holgate
Regarding your first sentence, isn't that whaI suggested in my second para? BTW, are you the Len Holgate that used to (or still does) contract at CSFP?
anon
Autopulated
@Neil: Obviously with the phrase "should I use values?" I was meaning both member and ctor, but thanks for the clarification and for the answer :)
Salv0
+3  A: 

It's fine as long as the constructor either just uses it without retaining it, copies it for further use (at which point, using a reference probably doesn't matter), or assumes ownership of it (which is iffy because you're depending on the user to behave correctly and not use the string reference further).

However, in most cases, the string copy probably won't be a bottleneck and should be preferred for bug avoidance reasons. If, later, you can PROVE that it's a bottleneck (using profiling, for instance), you might want to think about fixing it.

Daniel Bruce
+1  A: 

While passing the parameter via a const reference is a nice thing (you should do that in most cases), storing it as a const reference is dangerous -- if the object passed ceases to exits, you might get a segfault.

Also remember -- premature optimization is the root of all evil! If you have performance issues after writing working code, use a tool like gprof to find where the bottleneck is. And from experience I can tell that the bottleneck almost always will be in bad design, and not a bad language use.

Kornel Kisielewicz
A: 

If you are highly concerned about performance then passing by reference is the better approach .

Think about following example to make picture more clear:

class A{
  public : A() {}
};

class B : public A{
  public : B() {}
};
class MyClass{

 B bObj; 
public: 
 MyClass(B b) : bObj(b) { } // constructor and destructor overhead
 MyClass(B &b) { }

};
Ashish
A: 

I agree with other people that you should be more concerned about correctness and robustness over performance (so the member variable should be a copy, not a reference) and that if you're really concerned about performance, you should profile your code.

That said, it's not always clear-cut that passing by const reference is faster. For example, if you pass by value instead and if the argument is an rvalue, the compiler can do copy elision (see http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/) and avoid an extra copy when you save it to the member variable. (This isn't very intuitive, and it's probably not something you'd want to do everywhere, so again: profile!)

jamesdlin
A: 

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:

  1. The reference is defined at construction, and won't ever be changed
  2. The reference is never supposed to be NULL or invalid

This is part of the contract.

paercebal