在成员函数中返回* this

时间:2011-02-04 15:34:46

标签: c++ class this

我最近使用了一个允许以下类型语法的库:

MyClass myObject;
myObject
    .setMember1("string value")
    .setMember2(4.0f)
    .setMember3(-1);

显然,这是通过让设置者返回MyClass&类型;类似return * this。我喜欢这段代码的样子,但我看不太多。当发生这种情况时,我通常会怀疑为什么。

那么,这是一种不好的做法吗?这样做有什么意义呢?

9 个答案:

答案 0 :(得分:6)

这有时称为Named Parameter Idiom或方法链接。这不是坏习惯,它有助于提高可读性。考虑从C ++ FAQ中取消的这个例子

File f = OpenFile("foo.txt")
            .readonly()
            .createIfNotExist()
            .appendWhenWriting()
            .blockSize(1024)
            .unbuffered()
            .exclusiveAccess();

另一种方法是使用OpenFile方法的位置参数,要求程序员记住每个参数的位置。

答案 1 :(得分:6)

有些人称之为流畅的编程(或流畅的界面)。其他人称之为混乱。

倾向于对后一阵营。特别是,我的经验是,在许多情况下,人们以这种方式编写代码依赖在“流畅的界面”上进行相当多的初始化对象。换句话说,尽管有伪装,它仍然是两步初始化。同样地,尽管在许多情况下它可能是可以避免的,但它似乎经常会导致相当一部分应该完全私有的类通过操纵器可以公开修改。

我个人更喜欢创建后对象是不可变的。这显然并非总是可行,在某些情况下,你甚至不能非常接近。尽管如此,你对外部操纵开放的对象的内部结构越多,你就越不确定这个对象是否保持连贯状态(通常,你需要做的工作就越多,以保持连贯的状态)。

答案 2 :(得分:4)

您的示例 命名参数idiom

使用命名参数idiom,可链式setter设置参数(参数)包的属性。

您的代码中有一堆用于修改您正在构建的最终对象的setter,称为两阶段构造

一般来说,两阶段构造只是Bad™,并且通过将属性暴露给客户端代码实现的两阶段构造(如您的示例中所示)是非常糟糕的。

例如,一般来说,一旦构建了该对象(并且可能打开了文件),您就不希望能够修改文件对象的属性。

干杯&第h。,

答案 3 :(得分:2)

这是一种常见的做法。 operator=的重载意味着链接调用:

class Foo {

public:
   Foo& operator=(const Foo& f) { 
      if (this != &f) { // check for self-assignment
         // do some stuff...
      }
      return *this;
   }

};

此代码允许执行以下操作:

Foo a, b, c;
a = b = c;

请注意,检查自行分配是强制性的,因为您经常需要在当前对象中取消分配内容,因此允许a = a会破坏您的代码。

根据@Fred Nurk的评论,我想补充一点,你应该看一下Copy-and-Swap习语,以避免代码重复并发出无异常代码。
有关更多信息,请查看下面的链接:

What is the copy-and-swap idiom?
http://gotw.ca/gotw/059.htm

答案 4 :(得分:1)

这种风格没问题。唯一的缺点是你不能将返回值用于更典型的目的,比如返回函数的结果。

答案 5 :(得分:1)

它被称为fluent api。这不是一种不好的做法,只是一种不同的编程风格。

最大的缺点(IMO)是因为你要返回对自己的引用,你不能返回任何其他内容,因为它们被编译器看作是一个巨大的“线”,所以很难调试流畅的语句代码。

答案 6 :(得分:1)

我不确定它是否被认为是不好的做法,但有一个含义是您不能再返回错误代码,因此您要么被迫使用异常或通过引用传入的丑陋的错误对象。例外情况有时是正确的解决方案,但往往不是因为它们在某些平台上抛出并且被禁用是昂贵的。

我真的认为这是一种风格化的东西,并且由于C ++在语法和文化方面与C的密切关系,许多C ++程序员喜欢错误代码,所以更愿意返回它们而不是返回引用。但是我也经常看到这种情况的回归,我不认为这是不好的做法。

答案 7 :(得分:1)

理论上,如果你做了类似的事情,你最终可能会有一个悬空参考:

MyClass *myObject = new MyClass;
MyClass & dangling = myObject->setMember1("string");
delete myObject;
dangling.setMember2(yrParam);

所以要注意这一点。

答案 8 :(得分:1)

不错的做法,事实上你经常会看到输出流,以便连接多个字符串和值。

我看到的唯一不利之处是它会阻止你返回任何其他内容,但如果它是一个set方法,那就不重要了。