构造函数接受字符串引用。馊主意?

时间:2009-12-19 11:21:01

标签: c++ oop constructor reference

它被认为是一个坏主意/糟糕的设计,有一个类接受引用的构造函数,如下所示?

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

还是应该使用值? 我实际上关心表现。

8 个答案:

答案 0 :(得分:16)

如果你在这种情况下使用了一个值参数,你会在类中引用一个临时引用,这在将来的某个时候会变得无效。

这里的坏主意可能是将引用存储为类中的成员。存储值几乎总是更简单,更正确。在这种情况下,传递构造函数一个const引用是正确的事情。

至于性能,你应该只关心这个重要的 ,你只能通过分析你的代码来找到它。您应该首先编写代码以获得正确性,其次是为了清晰起见,最后是为了表现。

答案 1 :(得分:3)

只要构造函数只是在不保留它的情况下使用它,将其复制以供进一步使用(此时,使用引用可能无关紧要),或者假设它的所有权(这是因为你',这是很好的)依赖于用户正确行为而不再使用字符串引用。)

但是,在大多数情况下,字符串副本可能不会成为瓶颈,因为出于避免错误的原因而应该首选。如果以后你可以证明它是一个瓶颈(例如使用分析),你可能想要考虑修复它。

答案 2 :(得分:2)

如果你可以保证引用所使用的字符串在该类之前不会超出范围,那么可能可以使用(我不会“T)。如果您遇到问题,可能最好使用引用计数智能指针传递字符串。

编写应用程序以使类构造函数复制字符串,然后在遇到性能问题时,对它们进行概要分析可能是值得的,也是更安全的。大多数情况下,这不是引起问题的事情,而是在算法和数据结构层面。

答案 3 :(得分:2)

虽然通过const引用传递参数是一件好事(在大多数情况下你应该这样做),将它存储为const引用是危险的 - 如果传递的对象停止退出,你可能会遇到段错误。 / p>

还要记住 - 过早优化是万恶之源!如果在编写工作代码后出现性能问题,请使用gprof之类的工具查找其中瓶颈。根据经验,我可以看出瓶颈几乎总是在糟糕的设计中,而不是糟糕的语言使用。

答案 4 :(得分:2)

我同意其他人的观点,你应该更关注正确性和稳健性而不是性能(所以成员变量应该是副本,而不是参考),如果你真的关心性能,你应该对代码进行分析

也就是说,通过const引用传递的速度并不总是很明确。例如,如果您通过值传递而且参数是rvalue,则编译器可以执行复制省略(请参阅 http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/)并在将其保存到成员变量时避免使用额外的副本。 (这不是很直观,而且可能不是你想要在任何地方做的事情,所以再次:个人资料!)

答案 5 :(得分:1)

如果您正在编写编译器,复制文件名一次或两次将不会成为瓶颈。这更像是一个C ++风格的问题,我将留给周围更多C ++精明的人。

答案 6 :(得分:1)

您的类必须是自包含的,并避免不必要的依赖。在您的示例中,您的“编译器”类将依赖于CurrentFileName字符串的整个存在。这意味着如果在编译器之前销毁了CurrentFileName,那么你就会遇到问题。

家属&管理者

所以,我猜这取决于Dependent类和Manager类之间依赖关系的性质(即Dependent类依赖于Manager类,或者,在您的示例中,Compiler类依赖于std :: string上课... ...

  • 如果依赖项为“soft”,则Dependent应复制Manager类。
  • 如果依赖项是“强”,则Dependent可以引用Manager类。

    软与强

“软依赖”的一个例子是你的Dependent只需要一个值,而不是确切的对象本身。字符串通常被视为一个值。

“强依赖”的一个例子是当您的Dependent需要访问其Manager时,或者如果没有管理员而且没有管理者没有意义(即如果管理器被销毁,那么所有家属应该在之前被销毁)< / p>

结论

通常,依赖性是软的。

如果有疑问,请认为它很软。你会遇到更少的问题(这是没有指针算术的漂亮的C ++段错误的方法之一),并且仍然有可能在需要时优化它

关于您的案例:软

帮自己一个忙,并复制一下这个价值。 优化是所有邪恶的根源,除非你的探查者说字符串的副本是个问题,然后制作副本,如下所示:

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

关于我的案例:强

现在,如果没有经理本身,你可能处于Dependent的存在没有意义的情况。

我目前在代码上工作,用户创建Notifier对象来订阅事件,并在需要时检索它们。 Notifier对象在构造时附加到Manager,并且不能与它分离。

向库用户强加管理器的时间超过通知程序是一种设计选择。这意味着以下代码:

Manager manager ;
Notifier notifier(manager) ;  // manager is passed as reference

通告程序代码与您建议的代码非常相似:

class Notifier
{
   public :
      Notifier(Manager & manager) : m_manager(manager) {}
   private :
      Manager & m_manager ;
} ;

如果仔细观察设计,m_manager将用作Manager对象的不可变指针。我使用C ++引用来确定:

  1. 参考在构造时定义,不会被更改
  2. 引用永远不应为NULL或无效
  3. 这是合同的一部分。

答案 7 :(得分:0)

如果您非常关注性能,那么通过引用传递是更好的方法。

考虑以下示例,使图片更清晰:

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

};