Возвращая * this в функциях-членах - PullRequest
13 голосов
/ 04 февраля 2011

Недавно я использовал библиотеку, которая допускает синтаксис следующего типа:

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

Очевидно, это достигается с помощью того, что установщики возвращают MyClass & type;что-то вроде возврата * это.Мне нравится, как выглядит этот код, но я не вижу его много.Когда это происходит, я обычно подозреваю, почему.

Итак, это плохая практика?Каковы некоторые из последствий этого, как это?

Ответы [ 9 ]

6 голосов
/ 04 февраля 2011

Некоторые люди называют это беглым программированием (или беглым интерфейсом).Другие называют это беспорядком.

Я склонен в некоторой степени к последнему лагерю.В частности, мой опыт показывает, что во многих случаях люди, пишущие код таким образом , зависят от "свободного интерфейса" для довольно небольшой инициализации объекта.Другими словами, несмотря на маскировку, это все же двухэтапная инициализация.Точно так же, хотя этого, вероятно, можно избежать во многих случаях, часто кажется, что это приводит к тому, что часть класса, который должен быть полностью приватным, становится публично модифицируемой с помощью манипуляторов.что объекты неизменны после создания.Это явно не всегда возможно, а в некоторых случаях вы даже не можете подойти очень близко.Тем не менее, чем больше внутренних объектов объекта вы открываете для внешних манипуляций, тем меньше у вас уверенности в том, что этот объект поддерживает согласованное состояние (и, как правило, вам нужно больше работать для поддержания согласованного состояния).

6 голосов
/ 04 февраля 2011

Это иногда называют Именом именованного параметра или цепочкой метода. Это не плохая практика, это может помочь читабельности. Рассмотрим этот пример, взятый из C ++ FAQ

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

Альтернативой может быть использование позиционных аргументов для метода OpenFile, требующее от программиста запоминания позиции каждого аргумента.

4 голосов
/ 04 февраля 2011

Ваш пример не именованные параметры идиома .

При использовании идиомы именованных параметров сеттеры устанавливают атрибуты пакета аргументов (параметров).

Вместо этого в вашем коде есть набор сеттеров для изменения конечного объекта, который вы создаете, который называется двухфазное построение .

В общем, двухфазное построение - это просто Bad & trade; и двухфазное построение, реализуемое путем предоставления атрибутов клиентскому коду, как в вашем примере, Very Bad & trade;

Например, вы вообще не хотите иметь возможность изменять атрибуты файлового объекта после того, как этот объект создан (и, возможно, файл открыт).

Приветствия & hth.,

2 голосов
/ 04 февраля 2011

Это обычная практика.Перегрузка 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-и-Swap идиома, чтобы избежать дублирования кода и выпускать код без исключений.
Посмотрите ссылки ниже для получения дополнительной информации:

Что такое идиома копирования и замены?
http://gotw.ca/gotw/059.htm

1 голос
/ 04 февраля 2011

Неплохая практика, на самом деле вы часто видите это с выходными потоками, чтобы объединить несколько строк и значений.

Единственный недостаток, который я вижу, заключается в том, что он не позволяет вам возвращать что-либо еще, хотя, если это метод set, это не должно иметь значения.

1 голос
/ 04 февраля 2011

Теоретически, вы можете получить висячую ссылку, если сделаете что-то ужасное, например:

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

Так что знайте об этом.

1 голос
/ 04 февраля 2011

Я не уверен, считается ли это плохой практикой или нет, но одним из следствий этого является то, что вы больше не можете возвращать коды ошибок, поэтому вы либо вынуждены использовать исключения, либо уродливые объекты ошибок, переданные по ссылке. Исключения иногда являются правильным решением, но часто это не так, потому что они могут быть дорогостоящими и отключены на некоторых платформах.

Я действительно считаю, что это стилистическая вещь, и из-за тесной связи C ++ с C по синтаксису и культуре многие программисты C ++ любят коды ошибок и поэтому предпочитают возвращать их, а не возвращать ссылку. Но я часто видел возвращение * этого, и я не думаю, что это плохая практика.

1 голос
/ 04 февраля 2011

Это называется свободный API .Это не плохая практика, просто другой стиль программирования.

Самый большой недостаток (IMO) состоит в том, что, так как вы возвращаете ссылку на себя, вы не можете вернуть что-либо еще, и это может быть трудно отладитьбеглые выражения, поскольку они рассматриваются компилятором как одна гигантская «строка» кода.

1 голос
/ 04 февраля 2011

Нет проблем с этим стилем.Единственным недостатком является то, что вы не можете использовать возвращаемое значение для более типичных целей, таких как возврат результата функции.

...