Возврат * в функции-член
Недавно я использовал библиотеку, которая допускает следующий тип синтаксиса:
MyClass myObject;
myObject
.setMember1("string value")
.setMember2(4.0f)
.setMember3(-1);
Очевидно, это достигается за счет того, что сеттеры возвращают MyClass и тип; что-то вроде return * this. Мне нравится, как выглядит этот код, но я его не вижу. Когда это происходит, я обычно подозрительно отношусь к тому, почему.
Итак, это плохая практика? Каковы некоторые из последствий этого?
Ответы
Ответ 1
Это иногда называют Именованный идентификатор параметра или цепочка методов. Это неплохая практика, она может помочь в удобочитаемости. Рассмотрим этот пример, снятый с часто задаваемых вопросов по С++
File f = OpenFile("foo.txt")
.readonly()
.createIfNotExist()
.appendWhenWriting()
.blockSize(1024)
.unbuffered()
.exclusiveAccess();
Альтернативой может быть использование позиционных аргументов метода OpenFile, требующих от программиста запоминания позиции каждого аргумента.
Ответ 2
Некоторые люди называют это свободное программирование (или свободный интерфейс). Другие называют это беспорядком.
Я несколько склоняюсь к последнему лагерю. В частности, мой опыт заключается в том, что во многих случаях люди, пишущие код таким образом, зависят от "свободного интерфейса" для довольно немного инициализации объекта. Другими словами, несмотря на маскировку, это еще двухэтапная инициализация. Точно так же, хотя во многих случаях это, вероятно, можно избежать, оно часто, как представляется, приводит к тому, что он должен быть полностью закрыт для класса, который становится общедоступным с помощью манипуляторов.
Лично я предпочитаю, чтобы объекты были неизменными после создания. Это явно не всегда возможно, и в некоторых случаях вы даже не можете приблизиться. Тем не менее, чем больше внутренних объектов, которые вы открываете для внешних манипуляций, тем меньше вероятность того, что вы станете объектом этого объекта, поддерживающим согласованное состояние (и, как правило, тем больше работы вы должны сделать, чтобы поддерживать согласованное состояние).
Ответ 3
Это обычная практика. Перегрузка 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, чтобы избежать дублирования кода и выпустить код без исключений.
Посмотрите ссылки по этому вопросу для получения дополнительной информации:
Что такое идиома копирования и свопинга?
http://gotw.ca/gotw/059.htm
Ответ 4
В вашем примере это не именованные параметры idiom.
С именованными параметрами idiom, привязываемые сеттеры устанавливают атрибуты пакета параметров (параметров).
Вместо этого у вас есть набор настроек для модификации конечного объекта, который вы создаете, называемого двухфазной конструкцией.
В целом двухфазная конструкция - это просто Bad & trade; и двухфазная конструкция, реализуемая путем выставления атрибутов клиентскому коду, как в вашем примере, является очень плохой и торгуемой.
Например, вы, как правило, не хотите изменять атрибуты файлового объекта, как только этот объект был создан (и, возможно, файл открыт).
Приветствия и hth.,
Ответ 5
Нет проблем с этим стилем. Единственным недостатком является то, что вы не можете использовать возвращаемое значение для более типичных целей, например, вернуть результат функции.
Ответ 6
Он назвал свободно api. Это неплохая практика, просто другой стиль программирования.
Самые большие недостатки (IMO) состоят в том, что, поскольку вы возвращаете ссылку на себя, вы не можете вернуть что-либо еще, и может быть сложно отладить беглое высказывание, поскольку они рассматриваются компилятором как одна гигантская "линия", кода.
Ответ 7
Я не уверен, считал ли он плохую практику или нет, но одним из последствий является то, что вы больше не можете возвращать коды ошибок, поэтому вы либо вынуждены использовать исключения или уродливые объекты ошибок, переданные по ссылке. Исключения иногда являются правильным решением, но часто это не так, поскольку они могут быть дорого брошены и отключены на некоторых платформах.
Я действительно думаю, что это стилистическая вещь, и из-за близких отношений С++ с C как в синтаксисе, так и в культуре многие программисты на С++, такие как коды ошибок, предпочитают возвращать их, а не возвращать ссылку. Но я часто видел возвращение * этого, и я не думаю, что это плохая практика.
Ответ 8
В теории вы могли бы получить оборванную ссылку, если вы сделаете что-то ужасное, как:
MyClass *myObject = new MyClass;
MyClass & dangling = myObject->setMember1("string");
delete myObject;
dangling.setMember2(yrParam);
Знайте об этом.
Ответ 9
Неплохая практика, на самом деле вы часто видите ее с выходными потоками, чтобы объединить несколько строк и значений.
Только недостаток, который я вижу, заключается в том, что он не позволяет вам возвращать что-либо еще, но если это метод набора, это не имеет значения.