Constructors accepting string reference. Bad idea?

狂风中的少年 提交于 2019-12-10 23:15:36

问题


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.


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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!)




回答6:


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.




回答7:


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.




回答8:


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) { }

};


来源:https://stackoverflow.com/questions/1932654/constructors-accepting-string-reference-bad-idea

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!