【问题标题】:Is returning references of member variables bad practice?返回成员变量的引用是不好的做法吗?
【发布时间】:2011-12-21 18:35:29
【问题描述】:

据说以下内容比将 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&)

【问题讨论】:

    标签: c++ reference member-variables


    【解决方案1】:

    答案取决于一个人想要做什么。返回引用是促进数据结构突变的一种便捷方式。一个很好的例子是 stl 映射。它返回对元素的引用,即

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

    没有什么能阻止你做事

    auto & aref = a[1];
    

    这一定是一种不好的做法吗?我不这么认为。我会说,如果你可以不用它就这样做。如果它让生活更方便、更高效,请使用它并注意您在做什么。

    【讨论】:

      【解决方案2】:

      返回对类内部的引用(或指针)不好的原因有很多。从(我认为是)最重要的开始:

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

      2. Invariant 不再可持续(在非常量引用的情况下):任何人都可以随意访问和修改引用的属性,因此您无法“监视”其更改。这意味着您不能维护该属性所属的不变量。从本质上讲,您的班级正在变成一个 blob。

      3. Lifetime 问题出现了:在它们所属的原始对象不复存在后,很容易保留指向该属性的引用或指针。这当然是未定义的行为。例如,大多数编译器都会尝试警告有关在堆栈上保留对对象的引用,但我知道没有编译器能够为函数或方法返回的引用生成此类警告:您只能靠自己。

      因此,通常最好不要放弃对属性的引用或指针。 甚至不是 const 的!

      对于较小的值,通常通过复制传递它们就足够了(inout),尤其是现在使用移动语义(正在传入)。

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

      最后,请注意,对于某些类,拥有公共成员并不是那么糟糕。封装 pair 的成员有什么意义?当你发现自己编写的类不过是属性的集合(没有任何不变量)时,不要让我们把所有的 OO 都放在我们身上并为它们中的每一个编写一个 getter/setter 对,而是考虑将它们公开。

      【讨论】:

      • +1,但提供访问器有一个相反的理由:您可以在调用中添加检测,这样可以更轻松地找到代码中正在更新对象的位置,或者附加调试器检测问题。
      • 供您参考,该示例讨论的是具有私有成员的类,因此是 bool deleted_;。我喜欢POD
      • @David:我并不反对 T get()/void set(T) 中的访问器,在其中您可以有效地跟踪值的变化,提供惰性计算和许多其他的东西,但唯一有趣的花絮是T&amp; access() 用于“跟踪”访问已进行,这并没有带来太多好处。
      • @acidzombie24: 没有什么能阻止你拥有一些公众和其他私人成员。
      • 为什么“通常最好不要放弃对属性的引用或指针。即使是 const 也不要”?
      【解决方案3】:

      如果template 类型TU 是大型结构,则按价值返回是昂贵的。但是,您是正确的,通过引用返回等同于授予对 private 变量的访问权限。要解决这两个问题,请将它们设为const 引用

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

      P.S. 此外,当没有 setter 方法时,在构造函数中保持变量未初始化是一种不好的做法。似乎在原始代码中,First()Second()first_second_ 的包装器,用于读/写两者。

      【讨论】:

      • 啊哈,很好的解决方案。只要读取 (First()) 有这么多的副作用,这完全没问题(不是不合理的限制,但仍然是限制:))
      • This link 对传递值和 R 值有一些有趣的见解。
      猜你喜欢
      • 2018-08-13
      • 1970-01-01
      • 1970-01-01
      • 2013-04-25
      • 2021-05-07
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多