在成员函数中返回 *this
我最近使用了一个允许以下类型语法的库:
MyClass myObject;
myObject
.setMember1("string value")
.setMember2(4.0f)
.setMember3(-1);
显然,这是通过让设置器返回 MyClass & 来完成的。类型;像 return *this 这样的东西。我喜欢这段代码的外观,但我不常看到它。当这种情况发生时,我通常会怀疑为什么。
那么,这是一个不好的做法吗?这样做有什么影响?
I recently used a library that allows the following type of syntax:
MyClass myObject;
myObject
.setMember1("string value")
.setMember2(4.0f)
.setMember3(-1);
Obviously this is accomplished by having the setters return a MyClass & type; something like return *this. I like the way this code looks, but I don't see it a lot. When that happens I'm usually suspicious as to why.
So, is this a bad practice? What are some of the implications of doing it like this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
这有时被称为命名参数惯用语或方法链接。这不是坏习惯,它可以提高可读性。考虑这个来自 C++ FAQ 的示例
另一种方法是使用 OpenFile 方法的位置参数,要求程序员记住每个参数的位置。
This is sometimes referred to as the Named Parameter Idiom or method chaining. This isn't bad practice, it can aid readability. Consider this example lifted from the C++ FAQ
The alternative would be to use positional arguments to the OpenFile method, requiring the programmer to remember the position of each argument.
有些人称之为流畅编程(或流畅界面)。其他人称之为一团糟。
我倾向于后一个阵营。特别是,我的经验是,在许多情况下,人们以这种方式编写代码依赖于“流畅的接口”来初始化对象。换句话说,尽管进行了伪装,但它仍然是两步初始化。同样,尽管在许多情况下它可能是可以避免的,但它似乎经常导致相当多本应完全属于类的私有内容通过操纵器公开修改。
就我个人而言,我更喜欢对象在创建后是不可变的。显然这并不总是可能的,在某些情况下你甚至无法非常接近。尽管如此,您对外部操作开放的对象内部越多,您对该对象保持一致状态的把握就越少(通常,您需要做更多的工作来维持一致状态)。
Some people call this fluent programming (or a fluent interface). Others call it a mess.
I tend somewhat toward the latter camp. In particular, my experience has been that in many cases, people writing code this way depend on the "fluent interface" for quite a bit of initializing an object. In other words, despite the disguise, it's still two-step initialization. Likewise, although it's probably avoidable in many cases, it frequently seems to result in quite a bit of what should be entirely private to the class being made publicly modifiable via manipulators.
Personally I prefer that objects are immutable after creation. That's clearly not always possible, and in some cases you can't even come very close. Nonetheless, the more of an object's internals that you make open to outside manipulation, the less certain you become about that object maintaining a coherent state (and, typically, the more work you have to do to maintain a coherent state).
您的示例不是命名参数习惯用法。
使用命名参数习惯用法,可链接设置器设置参数(参数)包的属性。
相反,您的代码有一堆用于修改您正在构造的最终对象的设置器,称为两阶段构造。
一般来说,两阶段构造只是 Bad™,而通过向客户端代码公开属性来实现的两阶段构造(如您的示例中)是 Very Bad™。
例如,一旦文件对象被构造(并且可能文件被打开),您通常不希望能够修改该对象的属性。
干杯&呵呵,
Your example is not the named parameters idiom.
With the named parameters idiom, the chainable setters set attributes of an argument (parameter) pack.
Your code instead has a bunch of setters for modifying the final object you're constructing, called two-phase construction.
In general two-phase construction is just Bad™, and two-phase construction implemented by exposing attributes to client code, as in your example, is Very Bad™.
For example, you do not, in general, want to be able to modify a file object's attributes, once that object has been constructed (and possibly file opened).
Cheers & hth.,
这是一种常见的做法。
operator=
的重载意味着对链调用执行此操作:此代码允许执行以下操作:
请注意,检查自分配是强制性的,因为您经常必须释放当前对象中的内容,因此允许
a = a
会破坏你的代码。根据 @Fred Nurk 的评论,我想补充一点,您应该看看复制和交换习惯用法,以避免代码重复并发出无异常代码。
请查看下面的链接以获取更多信息:
什么是复制和-交换习语?
http://gotw.ca/gotw/059.htm
It's a common practice. The overloading of
operator=
implies to do so to chain calls:This code allows to do things like:
Please note that checking for self-assignment is compulsory because you often have to deallocate stuffs in the current object, so allowing
a = a
would break your code.Following @Fred Nurk's comment, I'd like to add that you should have a look at the Copy-and-Swap idiom in order to avoid code duplication and issue an exception-free code.
Have a look at the links hereunder for more information:
What is the copy-and-swap idiom?
http://gotw.ca/gotw/059.htm
这种风格没有什么问题。唯一的缺点是您不能将返回值用于更典型的目的,例如返回函数的结果。
There's no problem with this style. The only downside is that you can't use the return value for more typical purposes, like returning the result of the function.
它被称为Fluent api。这并不是不好的做法,只是一种不同的编程风格。
最大的缺点(IMO)是,由于您返回的是对自己的引用,因此您无法返回任何其他内容,并且调试流畅的语句可能很困难,因为它们被编译器视为一大“行”代码。
It's called a fluent api. It's not bad practice, just a different style of programming.
The biggest drawbacks (IMO) would be that since you are returning a reference to yourself, you can't return anything else and it can be difficult to debug fluent statements since they are seen by the compiler as one giant "line" of code.
我不确定这是否被认为是不好的做法,但一个暗示是您不能再返回错误代码,因此您要么被迫使用异常,要么被迫使用通过引用传入的丑陋错误对象。异常有时是正确的解决方案,但通常不是,因为抛出异常的成本可能很高,并且在某些平台上被禁用。
我确实认为这是一种风格问题,并且由于 C++ 在语法和文化上与 C 的密切关系,许多 C++ 程序员喜欢错误代码,因此更喜欢返回它们而不是返回引用。但我也经常看到 *this 的返回,我不认为这是不好的做法。
I'm not sure if it's considered bad practice or not, but one implication is that you can no longer return error codes, so you're either forced to use exceptions or ugly error objects passed in by reference. Exceptions are sometimes the right solution, but often aren't since they can be expensive to throw and are disabled on some platforms.
I really think it's a stylistic thing though, and because of C++'s close relationship with C both in syntax and culture, many C++ programmers like error codes and so will prefer returning them instead of returning a reference. But I've seen return of *this often as well and I don't think it's bad practice.
不错的做法,事实上,您经常在输出流中看到它,以便连接多个字符串和值。
我看到的唯一缺点是它会阻止您返回任何其他内容,但如果它是一个 set 方法,那就不重要了。
Not bad practice, in fact you see it often with output streams in order to concatenate multiple strings and values.
Only disadvantage that I see is that it prevents you from returning anything else, though if it's a set method, it shouldn't matter.