我应该更喜欢成员数据中的指针还是引用?

这是一个简单的例子来说明这个问题:

class A {};


class B
{
B(A& a) : a(a) {}
A& a;
};


class C
{
C() : b(a) {}
A a;
B b;
};

所以 B 负责更新 C 的一部分,我通过 lint 运行代码,它对引用成员 林特 # 1725发牢骚。 这里说的是要小心处理默认的拷贝和赋值,这很公平,但是默认的拷贝和赋值对于指针来说也很糟糕,所以几乎没有什么好处。

我总是尝试在可以使用引用的地方使用引用,因为裸指针会不确定地引入谁负责删除那个指针。我更喜欢通过值嵌入对象,但是如果需要指针,我会在拥有指针的类的成员数据中使用 auto _ ptr,并将对象作为引用传递。

我通常只在成员数据中使用指针,当指针可以为空或可以更改时。对于数据成员而言,是否有其他原因更喜欢使用指针而不是引用?

是不是说一个包含引用的对象不应该被赋值,因为引用一旦初始化就不应该被更改?

110242 次浏览

是: 是不是说一个包含引用的对象不应该被赋值,因为引用一旦初始化就不应该被更改?

我对数据成员的经验法则是:

  • 永远不要使用引用,因为它会阻止赋值
  • 如果您的类负责删除,那么使用 Boost 的 scope _ ptr (它比 auto _ ptr 更安全)
  • 否则,请使用指针或常量指针

对象很少允许赋值和其他类似比较的东西。如果你考虑一些商业模式,比如“部门”、“员工”、“主管”,很难想象一个员工会被分配到另一个部门。

因此,对于业务对象,最好将一对一和一对多关系描述为引用而不是指针。

也许将一个或零关系描述为一个指针是可以的。

因此,没有’我们不能分配’然后因素。
许多程序员只是习惯使用指针,这就是为什么他们会找到任何参数来避免使用引用。

使用指针作为成员将迫使您或您的团队成员在使用之前反复检查指针,并使用“以防万一”注释。如果指针可以为零,那么指针可能被用作某种标志,这是不好的,因为每个对象都必须扮演自己的角色。

在一些重要的情况下,根本不需要可分配性。它们通常是轻量级的算法包装器,可以方便地进行计算,而不需要离开作用域。这些对象是引用成员的主要候选对象,因为您可以确保它们始终保存 有效引用,而且永远不需要复制。

在这种情况下,确保赋值操作符(通常也包括复制建构子操作符)不可用(通过从 boost::noncopyable继承或声明它们为 private)。

但是,正如用户 pt 已经注释的那样,大多数其他对象的情况并非如此。在这里,使用引用成员可能是一个巨大的问题,通常应该避免。

我通常只在成员数据中使用指针,当指针可以为空或可以更改时。对于数据成员而言,是否有其他原因更喜欢使用指针而不是引用?

是的。代码的可读性。指针使成员是引用(具有讽刺意味的是:)而不是包含的对象这一点更加明显,因为当您使用它时,您必须解除对它的引用。我知道有些人认为这是过时的,但我仍然认为它只是防止混淆和错误。

避免引用成员,因为它们限制了类的实现可以做的事情(包括,如您所提到的,阻止赋值操作符的实现) ,并且对类可以提供的内容没有任何好处。

例题:

  • 你被迫在每个构造函数的初始化列表中初始化引用: 没有办法将这个初始化过程分解成另一个函数(直到 C + + 0x 编辑: C + + 现在有了 委派构造函数)
  • 引用不能被反弹或为空。这可能是一个优势,但是如果代码需要更改以允许重新绑定,或者成员为 null,那么成员的所有用法都需要更改
  • 与指针成员不同,引用不能像重构可能需要的那样轻易地被智能指针或迭代器替换
  • 无论何时使用引用,它看起来像值类型(.操作符等) ,但其行为像指针(可以悬挂)——因此,例如 谷歌风格指南不鼓励使用引用

我自己的经验法则是:

  • 当你希望你的对象的生命依赖于其他对象的生命时,使用一个引用成员: 这是一种明确的方式来说明你不允许对象在没有其他类的有效实例的情况下生存——因为没有赋值和通过构造函数获得引用初始化的义务。你只是假设他们的生活与其他实例直接相关。它允许您稍后更改使用类实例的方式(带有 new,作为本地实例,作为类成员,由管理器中的内存池生成,等等)
  • 在其他情况下使用指针 : 当您希望稍后更改成员时,使用指针或常量指针以确保只读取指向的实例。如果该类型应该是可复制的,则无论如何都不能使用引用。有时候你也需要在一个特殊的函数调用之后初始化成员(例如 init ()) ,然后你别无选择只能使用一个指针。但是: 在所有成员函数中使用断言来快速检测错误的指针状态!
  • 如果你希望对象的生命周期依赖于外部对象的生命周期,并且你也需要这个类型是可复制的,那么在构造函数 中使用指针成员但是引用参数。这样你就在构造上指出这个对象的生命周期依赖于参数的生命周期,但是实现使用指针仍然是可复制的。只要这些成员只是通过拷贝来改变,而你的类型没有缺省构造函数,那么你的类型应该同时满足这两个目标。

似乎每个人都在分发一般规则,我提供两个:

  • 永远不要使用引用作为类成员。我从来没有在我自己的代码中这样做过(除了向自己证明我在这个规则中是正确的) ,并且无法想象我会这样做的情况。语义太混乱了,而且引用的设计目的并不是为了这个。

  • 在向函数传递参数时(基本类型除外) ,或者在算法需要副本时,始终使用引用。

这些规则很简单,对我很有帮助。我将使用智能指针(但是请不要使用 auto _ ptr)作为类成员的规则留给其他人。

我建议不要使用引用数据成员,因为您永远不知道谁将从您的类派生,以及他们可能想要做什么。它们可能不想使用被引用的对象,但是作为引用,您强制它们提供一个有效的对象。 我已经对自己做了足够多的停止使用参考数据成员。