使用引用作为返回值被视为错误的编码风格?

时间:2016-01-30 10:59:20

标签: c++ return-type

我对这两种设置值的可能性提出了疑问:

让我们说我得到了一个我要改变的课程。我正在使用这个:

void setfunc(std::string& st) { this->str = st; }

另一个能够完全相同的函数,但是使用字符串引用而不是用于设置值的void:

std::string& reffunc() { return this->str; }

现在,如果我要设置一个我可以使用的值:

std::string text("mytext");
setfunc(text);
//or
reffunc() = text;

现在我的问题是,如果使用设置值的第二种形式被认为是不好的。 没有性能差异。

4 个答案:

答案 0 :(得分:3)

首先有getter和setter的原因是该类可以保护其不变量并且更容易修改。

如果您只有按值返回的setter和getter,那么您的类具有以下自由,而不会破坏API:

  • 更改内部表示。也许字符串以不同的格式存储,更适合内部操作。也许它并没有存储在课堂上。
  • 验证传入的值。字符串是否具有最大或最小长度?设定者可以强制执行此操作。
  • 保留不变量。如果字符串发生变化,是否需要更改该类的第二个成员?设定者可以执行更改。也许字符串是一个URL,并且该类缓存了一些关于它的信息。 setter可以清除缓存。

如果更改getter以返回const引用,就像有时保存副本一样,则会失去一些表示自由。您现在需要一个可以引用的返回类型的实际对象,其寿命足够长。您需要为返回值添加生命周期保证,例如承诺在使用非const成员之前,引用不会失效。您仍然可以返回对不是该类的直接成员的对象的引用,但可能是成员的成员(例如,返回对内部名称struct的第一个名称部分的引用)或者是一个解除引用的指针。

但是如果你通过非const引用返回,几乎所有的赌注都会被取消。由于客户端可以更改引用的值,因此在值更改时,您不能再依赖于被调用的setter和由正在执行的类控制的代码。您无法约束该值,也无法保留不变量。通过非const引用返回使得该类与具有公共成员的简单结构略有不同。

这导致我们选择最后一个选项,只需将数据成员公开。与返回非const引用的getter相比,您唯一丢失的是返回的对象不再是间接成员;它必须是一个直接的,真实的成员。

另一方面,性能和代码开销。每个getter和setter都是要编写的附加代码,还有额外的错误机会(为一个成员复制粘贴一个getter / setter对,为另一个成员创建相同的内容,然后忘记更改其中的一个变量?)。编译器可能会内联访问器,但如果它没有,则会产生调用开销。如果按值返回类似字符串的内容,则必须复制它,这很昂贵。

这是一种权衡,但大多数情况下,编写访问器会更好,因为它可以为您提供更好的封装和灵活性。

答案 1 :(得分:2)

我们无法看到成员str的定义。 如果它是私有的,则您的reffunc()会公开对私人会员的引用;你正在写一个违反你设计的功能,所以你应该重新考虑你正在做的事情。 此外,它是一个引用,因此在使用该引用时,您必须确保包含str的对象仍然存在。 此外,您正在展示外部实施细节,可能会在未来发生变化,更改界面本身(如果str变得不同,setfunc()的实施可以进行调整,{{1}签名必须改变。)

你所写的并没有错,但它可能以错误的方式使用。你正在减少封装。这是一个设计选择。

答案 2 :(得分:1)

没关系。但是,你必须注意这些陷阱:

  • 引用的对象是可修改的。当您返回非const引用时,您将公开数据而不会受到修改的保护。显而易见,但无论如何要注意这一点!

  • 引用的对象可以脱离sope。如果引用的对象的生命周期结束,访问引用将是未定义的行为。但是,它们可以用来延长临时工作的寿命。

答案 3 :(得分:1)

您使用reffunc()函数的方式被认为是错误的编码。但是(正如评论中所提到的),一般来说,返回引用并不是错误的编码。

这就是reffunc() = text;被视为错误编码的原因:

人们通常不希望在作业的左侧进行函数调用,而是在右侧。查看函数调用时的自然期望是它计算并返回一个值(或rvalue,预计在赋值的右侧)而不是引用(或左值,预计在左侧)任务的一方)。

因此,通过在赋值的左侧放置函数调用,您将使代码更复杂,因此可读性更低。请记住,你没有任何其他动机(正如你所说,性能是相同的,通常是在这些情况下),良好的编码建议你使用" set"功能

阅读好书"Clean Code",了解有关清洁编码的更多问题。

至于函数中的返回引用,这是问题的标题,它并不总是编码错误,有时需要更清晰,更简洁的代码。特别是如果你返回一个引用(参见std :: vector中的operator []和通常帮助代码变得更易读和更简单的赋值运算符),c ++中的许多运算符重载功能都能正常工作。请参阅注释。