【问题标题】:'const string& getName() const{}' vs 'string getName() const{}'? [duplicate]'const string& getName() const{}' vs 'string getName() const{}'? [复制]
【发布时间】:2015-02-11 02:02:28
【问题描述】:

现在,这是高度概念化的。我不知道我是否理解正确,所以请帮助我理解其中的区别。

假设name 是一个私有的std::string 数据成员,由getName() 访问器函数访问:

const string& getName() const {
       return name;  
}

现在,这将返回一个对name 的引用,这只是别名的另一个词。因此,正在返回一个别名,即正在返回 name 数据成员。这是允许的还是会破坏数据隐藏的整个目的?

也就是说,上述方法与常规方法到底有什么不同:

string getName() const {
     return name;
}

???

最后,用前者代替后者真的值得吗?

【问题讨论】:

  • 不要。 [[[[[[[[[[[[[[[[[[[
  • 你想说什么?
  • 一个是副本,一个是参考
  • @JamesMoore:怎么样,一个是副本,一个是参考副本?
  • 另一个需要考虑的是,你真的需要一个 getter 函数吗?

标签: c++


【解决方案1】:

首先,如果底层值可以改变,特别是在多线程执行的上下文中,引用确实会出现问题。因此,数据成员的值在对象的生命周期内不会改变几乎是一个基本假设。它实际上是一个常数。

现在,参考的一个主要问题是它公开了实现细节,因此很难更改实现。

一个更具学术性的问题是它可能会破坏代码,如果之前有按值返回,或者只是因为它不寻常。例如

const string s& = foo().name();

foo() 按值返回对象,name() 按引用返回字符串,这为您提供了一个悬空引用,而不是天真的预期的延长生命周期。我称它为学术,因为我无法想象有人会这样写。尽管如此,墨菲定律等等。

它可能不会(显着)比值返回更有效,正是因为它不太可能仅用于初始化引用。

所以:

  • 效率可能不会显着提高,

  • 轻松防止更改实现,

  • 还有一个学术问题,产生悬空引用。

总之,别这样。

这是过早的优化和复杂化。

【讨论】:

  • 它可能不会比值返回更有效,正是因为它不太可能仅用于初始化引用 - 我强烈不同意这一点。它被称为name 的事实意味着它很可能用于比较、打印/记录、作为地图中的键等。大多数这些用途更有效。现在,您的学术示例只是在推动它......您所要做的就是string s =,您的问题就会消失,它变得与按价值解决方案一样(无效)效率。
  • @Rado:让我们将我的声明修改为“显着提高效率”。使用包含 58112 个小写单词的字典,在 std::map<string, int> 中查找“快速”一词 100 万次,使用 MinGW g++ 4.9.1 和 -O2 我得到(作为典型示例)0.117085 秒的值返回,并且0.109069 秒过去了 ref-returns。使用 Visual C++ 13.0 和 /O2 我得到 0.218411 秒的值返回 0.200394 秒的引用返回。恕我直言,这是一个非常边缘的优化,绝对不值得。代码在 (codepad.org/n7rkUc9T)。
【解决方案2】:

第一个允许调用者直接访问您的内部名称变量。授予它是常量,所以他们只能在其上调用 const 方法。但是您仍然希望外部调用者对您隐藏的内部数据进行操作吗?更糟糕的是,如果某个笨蛋决定对字符串的内部数据缓冲区进行 const_cast 并对其进行破解怎么办?

第二个返回您的内部名称变量的副本。任何来电者使用都非常安全。

我通常避开第一种类型,除了琐碎的低级类型。但是无论如何,琐碎的低级类型并没有太多的复制开销。所以这意味着我从不写那样的东西。

【讨论】:

    【解决方案3】:

    const 引用返回更好,因为它不会复制字符串。我之所以这么说是因为这种方式的接口更加灵活——如果需要,你可以随时将 const 引用复制到另一个字符串中,或​​者你可以将它用作引用——直到调用者。按值返回成员,您总是被复制。如果name 很大或经常使用,那么它会影响性能,我认为性能是您首先使用 C++ 的原因之一。

    现在,其他答案提出了一些关于返回 const 引用的负面观点,我认为这是无效的。

    您可以抛弃 const 的担忧是有道理的,但抛弃 const 只是 C++ 开发人员工具箱中的工具之一。为什么要把它拿走?如果有人真的想弄乱你的对象,他们总是可以在 C++ 中通过直接寻址内存来做到这一点,所以设计你的代码来拯救你的调用者是没有意义的。放弃 const 表明这样做的意图,在我看来是完全可以的。这意味着调用者有一些非常具体的理由这样做,并且知道被丢弃的 const 是针对非 const 对象的,因此是安全的。

    另一个答案中的学术例子很愚蠢:

    const string s& = foo().name();
    

    再次强调,设计您的代码以试图将调用者从自身中拯救出来会限制您使用 C++ 的强大功能。如果一个人真的想这样做,正确的方法是

    string s = foo().name();
    

    所以这一点也没有实际意义。

    唯一有效的一点是它在一定程度上暴露了实现。然而,在我看来,效率提升超过了这种担忧。

    您真正应该问自己的是 - 使用 name() 的通常情况是什么?

    通过回答这个问题,您将回答您应该使用哪种口味。

    对我来说,它被称为name 意味着它将主要用于打印/记录和比较。因此,这里的 const 引用显然是赢家。

    另外,看看那里的风格指南。他们中的大多数将让您通过 const 引用传递并通过 const 引用返回成员。如上所述,这样做有充分的理由。

    【讨论】:

    • “更好”是见仁见智,每个版本都有其优点和缺点。除了是否制作副本之外,还有其他考虑因素。它还可能取决于对象的最常见用例。
    • @MattMcNabb。没错,我用更好的解释编辑了我的答案......没有双关语;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 2010-12-29
    • 2016-08-28
    • 2016-06-25
    • 1970-01-01
    • 2011-01-07
    相关资源
    最近更新 更多