它被认为是一个坏主意/糟糕的设计,有一个类接受引用的构造函数,如下所示?
class Compiler
{
public:
Compiler( const std::string& fileName );
~Compiler();
//etc
private:
const std::string& m_CurrentFileName;
};
还是应该使用值? 我实际上关心表现。
答案 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上课... ...
“软依赖”的一个例子是你的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 ++引用来确定:
这是合同的一部分。
答案 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) { }
};