Аргумент по умолчанию
Эта статья нуждается в дополнительных цитатах для проверки . ( май 2009 г. ) |
В компьютерном программировании аргумент по умолчанию — это аргумент функции , который программисту не требуется указывать. В большинстве языков программирования функции могут принимать один или несколько аргументов. Обычно каждый аргумент должен быть указан полностью (это относится к языку программирования C). [1] ). Более поздние языки (например, C++ ) позволяют программисту указывать аргументы по умолчанию, которые всегда имеют значение, даже если оно не указано при вызове функции.
Аргументы по умолчанию в C++
[ редактировать ]Рассмотрим следующее объявление функции:
int MyFunc(int a, int b, int c = 12);
Эта функция принимает три аргумента, из которых последний имеет значение по умолчанию двенадцать. Программист может вызвать эту функцию двумя способами:
int result = MyFunc(1, 2, 3);
result = MyFunc(1, 2);
В первом случае значение аргумента c указывается явно. значение по умолчанию 12 Во втором случае аргумент опускается и вместо него будет использоваться . Для вызываемой функции невозможно узнать, был ли аргумент указан вызывающей стороной или использовалось значение по умолчанию.
Вышеупомянутый метод особенно полезен, когда нужно установить критерии по умолчанию, чтобы функцию можно было вызывать с параметрами или без них. Учтите следующее:
void PrintGreeting(std::ostream& stream = std::cout) {
// This outputs a message to the given stream.
stream << "hello world!";
}
Вызов функции:
PrintGreeting();
по умолчанию напечатает « hello world !» на стандартный вывод std::cout
(обычно экран). С другой стороны, любой объект типа std::ostream
теперь можно передать той же функции, и функция будет печатать в заданный поток, а не в стандартный вывод. В приведенном ниже примере задается std::ostream&
к std::cerr
и, таким образом, печатает выходные данные в виде стандартного потока ошибок.
PrintGreeting(std::cerr);
Поскольку значения аргументов по умолчанию «заполняются» на месте вызова, а не в теле вызываемой функции, виртуальные функции берут значения аргументов по умолчанию из статического типа указателя или ссылки, через которую выполняется вызов, а не из динамического типа объекта, предоставляющего тело виртуальной функции.
struct Base {
virtual std::pair<int, int> Foo(int x = 1) {
return {x, 1};
}
};
struct Derived : public Base {
std::pair<int, int> Foo(int x = 2) override {
return {x, 2};
}
};
int main() {
Derived d;
Base& b = d;
assert(d.Foo() == std::make_pair(2, 2));
assert(b.Foo() == std::make_pair(1, 2));
}
Перегруженные методы
[ редактировать ]Некоторые языки, например Java , не имеют аргументов по умолчанию. Однако то же поведение можно смоделировать, используя перегрузку методов для создания перегруженных методов с тем же именем, которые принимают разное количество аргументов; а версии с меньшим количеством аргументов просто вызывают версии с большим количеством аргументов, используя аргументы по умолчанию в качестве отсутствующих аргументов:
int MyFunc(int a, int b) { return MyFunc(a, b, 12); }
int MyFunc(int a, int b, int c) { /* main implementation here */ }
Однако, помимо нескольких других недостатков , поскольку аргументы по умолчанию не моделируются в системе типов, тип обратного вызова (также известный как функция более высокого порядка) не может выразить, что он принимает какую-либо из перегрузок, или имитировать аргументы по умолчанию с помощью перегруженные функции. В то время как в JavaScript определение неперегруженной функции может заменить значение по умолчанию, когда входное значение равно undefined
(независимо от того, было ли это неявно undefined
из-за отсутствия аргумента на месте вызова или явно переданного undefined
ценить); который моделируется как необязательный тип параметра аргумента ?:
в TypeScript . Решение JavaScript не разрешается статически (т. е. не во время компиляции, поэтому TypeScript моделирует только необязательность, а не значения по умолчанию в сигнатуре типа функции), что влечет за собой дополнительные накладные расходы во время выполнения, хотя и обеспечивает большую гибкость, поскольку обратные вызовы могут независимо друг от друга выполняться. контролируют их значения по умолчанию, а не централизованно диктуются сигнатурой типа (сигнатуры обратного вызова в) сигнатуре типа функции, которая вводит обратный вызов. Решение TypeScript можно смоделировать на Java с помощью Optional
тип, кроме аналога неявного undefined
для каждого отсутствующего аргумента является явным Optional.<Integer>absent()
на месте вызова.
Оценка
[ редактировать ]Для каждого вызова функции значения аргументов по умолчанию должны передаваться вызываемой функции. Если значение аргумента по умолчанию содержит побочные эффекты, это важно, когда эти побочные эффекты оцениваются — один раз для всей программы (во время анализа, компиляции или загрузки) или один раз для каждого вызова функции, во время вызова.
Python — это известный язык, который оценивает выражения в аргументах по умолчанию один раз, во время вычисления объявления функции. Если требуется оценка для каждого вызова функции, ее можно воспроизвести, задав аргументом по умолчанию контрольное значение , например None
, а затем тело функции оценивает побочные эффекты значения по умолчанию, только если было передано контрольное значение.
Например:
import random
def eager(a=random.random()):
return a
x = eager()
y = eager()
assert x == y
def lazy(a=None):
if a is None:
a = random.random()
return a
x = lazy()
y = lazy()
assert x != y
Степень
[ редактировать ]Обычно аргумент по умолчанию ведет себя идентично аргументу, переданному параметром, или локальной переменной, объявленной в начале функции, и имеет ту же область действия и размер (время жизни), что и параметр или другая локальная переменная, а именно автоматическая переменная , которая освобождается. по завершению функции.
В других случаях аргумент по умолчанию может быть выделен статически. Если переменная является изменяемой, она сохранит свое значение при вызовах функций, как и статическая переменная .
Такое поведение встречается в Python для изменяемых типов, таких как списки. Как и в случае с оценкой, чтобы обеспечить тот же размер, что и у локальной переменной, можно использовать контрольное значение:
def eager(a=[]):
return a
x = eager()
x += [1]
assert eager() == [1]
def lazy(a=None):
if a is None:
a = []
return a
x = lazy()
x += [1]
assert lazy() == []
Ссылки
[ редактировать ]- ^ Лесли, Мартин. «Параметры по умолчанию» . Справочник по программированию на C++ . Архивировано из оригинала 9 октября 2011 года . Проверено 13 января 2012 г.