返回成员变量的引用是不好的做法吗?

Is returning references of member variables bad practice?

本文关键字:成员 变量 引用 返回      更新时间:2023-10-16

据说下面的方法比First()和Second()作为公共成员更好。我相信这几乎一样糟糕。

// Example 17-3(b): Proper encapsulation, initially with inline accessors. Later
// in life, these might grow into nontrivial functions if needed; if not, then not.
template<class T, class U>
class Couple {
public:
  Couple()           : deleted_(false) { }
  void MarkDeleted() { deleted_ = true; }
  bool IsDeleted()   { return deleted_; }
private:
 T first_;
 U second_;
 bool deleted_;
 T& First()         { return first_; }
 U& Second()        { return second_; }
};

如果你提供了一种方法来访问类之外的私有变量,那么有什么意义呢?函数不应该是

T First(); void(or T) First(const T&)

将引用(或指针)返回到类的内部是不好的,有几个原因。从(我认为)最重要的开始:

  1. 封装被破坏:你泄露了一个实现细节,这意味着你不能再随心所欲地改变你的类内部。例如,如果您决定不存储first_,而是动态地计算它,那么如何返回对它的引用呢?

  2. 不变量不再是可持续的(在非const引用的情况下):任何人都可以随意访问和修改引用的属性,因此您无法"监视"其更改。这意味着您不能维护包含此属性的不变量。从本质上讲,你的类变成了一个blob。

  3. Lifetime问题出现了:在属性所属的原始对象不存在之后,很容易保留对属性的引用或指针。这当然是未定义的行为。例如,大多数编译器都会尝试对在堆栈中保留对象的引用发出警告,但据我所知,没有编译器能够对函数或方法返回的引用产生这样的警告:您可以自己处理。

因此,通常最好不要给出指向属性的引用或指针。连const都没有!

对于较小的值,通常通过复制传递它们就足够了(包括inout),特别是现在有了move语义(在进入的路上)。

对于较大的值,这确实取决于情况,有时代理可能会减轻您的麻烦。

最后,请注意,对于某些类,拥有公共成员并不是那么糟糕。封装pair的成员有什么意义?当您发现自己编写的类只不过是一组属性(没有任何不变式)时,与其让所有的OO都在我们身上,并为每个属性编写getter/setter对,不如考虑将它们设为public。

如果template类型TU是大结构体,那么按值返回是昂贵的。然而,你是正确的,通过引用返回相当于给予访问private变量。为了解决这两个问题,将它们设置为 const引用:

const T& First()  const  { return first_; }
const U& Second() const  { return second_; }

注:此外,当没有setter方法时,在构造函数中保持变量未初始化是一种不好的做法。在原始代码中,First()Second()似乎是first_second_的包装器,它们意味着读/写。

答案取决于你想做什么。返回引用是促进数据结构变化的一种方便方法。一个很好的例子就是静态地图。它返回对元素的引用,即

std::map<int,std::string> a;
a[1] = 1;

没有什么可以阻止你做

auto & aref = a[1];

这一定是不好的做法吗?我不这么认为。我想说的是,如果你可以没有它,那就这样做。如果它使生活更方便和高效,使用它,并意识到你在做什么。