这是一个简化的例子来说明这个问题:
class A {};
class B
{
B(A& a) : a(a) {}
A& a;
};
class C
{
C() : b(a) {}
A a;
B b;
};
所以 B 负责更新 C 的一部分。我通过 lint 运行代码,它抱怨引用成员:lint#1725。这谈到了照顾默认副本和分配这很公平,但是默认副本和分配对于指针也很糟糕,所以那里几乎没有优势。
我总是尽可能地尝试使用引用,因为裸指针不确定地引入了谁负责删除该指针。我更喜欢按值嵌入对象,但如果需要指针,我会在拥有指针的类的成员数据中使用 auto_ptr,并将对象作为引用传递。
当指针可能为空或可能更改时,我通常只会在成员数据中使用指针。是否有任何其他理由更喜欢指针而不是数据成员的引用?
是否可以说包含引用的对象不应该是可分配的,因为引用一旦初始化就不应更改?
我自己的经验法则:
当您希望对象的生命周期依赖于其他对象的生命周期时,请使用引用成员:这是一种明确的方式,表示您不允许对象在没有另一个类的有效实例的情况下处于活动状态 - 因为没有赋值和通过构造函数获取引用初始化的义务。这是一种设计类的好方法,无需假设它的实例是否属于另一个类。您只假设他们的生活与其他实例直接相关。它允许您稍后更改使用类实例的方式(使用新的、作为本地实例、作为类成员、由管理器中的内存池生成等)
在其他情况下使用指针:当您希望稍后更改成员时,请使用指针或 const 指针以确保仅读取指向的实例。如果该类型应该是可复制的,那么无论如何您都不能使用引用。有时您还需要在特殊函数调用(例如 init() )之后初始化成员,然后您别无选择,只能使用指针。但是:在所有成员函数中使用断言来快速检测错误的指针状态!
如果您希望对象生命周期依赖于外部对象的生命周期,并且您还需要该类型是可复制的,则在构造函数中使用指针成员但引用参数这样您在构造时表明该对象的生命周期取决于在参数的生命周期中,但实现使用指针仍然是可复制的。只要这些成员仅通过副本更改,并且您的类型没有默认构造函数,该类型就应该满足这两个目标。
避免引用成员,因为它们限制了类的实现可以做什么(包括,正如你提到的,阻止赋值运算符的实现)并且对类可以提供的功能没有任何好处。
示例问题:
您被迫在每个构造函数的初始化列表中初始化引用:无法将此初始化分解到另一个函数中(直到 C++0x,无论如何编辑:C++ 现在有委托构造函数)
引用不能被反弹或为空。这可能是一个优势,但如果代码需要更改以允许重新绑定或成员为空,则成员的所有用途都需要更改
与指针成员不同,引用不能轻易地被智能指针或迭代器替换,因为重构可能需要
每当使用引用时,它看起来像值类型(. 运算符等),但行为像指针(可以悬空) - 例如 Google 样式指南不鼓励它
对象很少应该允许分配和其他类似比较的东西。如果您考虑一些具有“部门”、“员工”、“主管”等对象的业务模型,则很难想象将一名员工分配给其他员工的情况。
因此,对于业务对象,将一对一和一对多关系描述为引用而不是指针是非常好的。
也许可以将一或零关系描述为指针。
所以没有'我们不能分配'然后因素。许多程序员只是习惯了指针,这就是为什么他们会找到任何参数来避免使用引用。
将指针作为成员将迫使您或您的团队成员在使用前一次又一次地检查指针,并带有“以防万一”的注释。如果指针可以为零,那么指针可能被用作一种标志,这很糟糕,因为每个对象都必须扮演自己的角色。
Use references when you can, and pointers when you have to.
在一些重要的情况下,根本不需要可分配性。这些通常是轻量级算法包装器,可以在不超出范围的情况下方便计算。此类对象是引用成员的主要候选对象,因为您可以确定它们始终拥有有效的引用并且永远不需要复制。
在这种情况下,请确保使赋值运算符(通常还有复制构造函数)不可用(通过从 boost::noncopyable
继承或将它们声明为私有)。
然而,正如用户 pts 已经评论的那样,对于大多数其他对象来说,情况并非如此。在这里,使用引用成员可能是一个大问题,通常应该避免。
由于每个人似乎都在发布一般规则,我将提供两个:
永远不要使用使用引用作为类成员。我从来没有在我自己的代码中这样做过(除了向自己证明我在这条规则中是正确的)并且无法想象我会这样做的情况。语义太混乱了,这真的不是引用的设计目的。
在将参数传递给函数(基本类型除外)或算法需要副本时,始终使用引用。
这些规则很简单,对我很有帮助。我将使用智能指针(但请不要使用 auto_ptr)作为类成员的规则留给其他人。
是的:是否说包含引用的对象不应该是可分配的,因为引用一旦初始化就不应更改?
我对数据成员的经验法则:
永远不要使用引用,因为它会阻止分配
如果您的班级负责删除,请使用 boost 的 scoped_ptr(比 auto_ptr 更安全)
否则,使用指针或常量指针
当指针可能为空或可能更改时,我通常只会在成员数据中使用指针。是否有任何其他理由更喜欢指针而不是数据成员的引用?
是的。代码的可读性。指针使成员更明显是一个引用(讽刺的是:)),而不是一个包含的对象,因为当您使用它时,您必须取消引用它。我知道有些人认为这是过时的,但我仍然认为它只是防止混淆和错误。
我建议不要引用数据成员,因为您永远不知道谁将从您的类中派生以及他们可能想要做什么。他们可能不想使用被引用的对象,但是作为一个引用你已经强迫他们提供一个有效的对象。我已经对自己做了足够多的事情来停止使用引用数据成员。
不定期副业成功案例分享
nullptr
,而引用成员变量将被保留。