Что такое «закрытие»?

std :: vector попытается выделить память для хранения вашего типа. Если ваш класс является чисто виртуальным, вектор не может знать размер класса, который он должен будет выделить.

Я думаю, что с вашим обходным путем вы сможете скомпилировать vector<IFunnyInterface>, но вы выиграли ' t иметь возможность манипулировать FunnyImpl внутри него. Например, если IFunnyInterface (абстрактный класс) имеет размер 20 (я действительно не знаю), а FunnyImpl имеет размер 30, потому что у него больше членов и кода, вы в конечном итоге попытаетесь поместить 30 в свой вектор из 20

Решение заключалось бы в том, чтобы выделить память в куче с помощью «новых» и указателей хранения в vector<IFunnyInterface*>

381
задан 11 revs, 7 users 50% 2 October 2012 в 15:51
поделиться

7 ответов

ответ Kyle довольно хорош. Я думаю, что единственное дополнительное разъяснение состоит в том, что закрытие является в основном снимком стека в точке, что функция лямбды создается. Тогда, когда функция повторно выполняется, стек восстанавливается тому состоянию прежде, чем выполнить функцию. Таким образом как Kyle упоминает, то скрытое значение (count) доступно, когда функция лямбды выполняется.

55
ответ дан Community 2 October 2012 в 15:51
поделиться

Я дам пример (в JavaScript):

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

то, Что делает эта функция, makeCounter, является ею, возвращает функцию, которую мы вызвали x, который подсчитает к одному каждому разу его названный. Так как мы не предоставляем параметров x, он должен так или иначе помнить количество. Это знает, где найти его на основе того, что назвало лексический обзор - это должно смотреть на пятно, где это определяется для нахождения значения. Это "скрытое" значение - то, что называют закрытием.

Вот мой приправляющий карри пример снова:

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

то, Что Вы видите, - то, что, когда Вы звоните, добавляют с параметром (который равняется 3), что значение содержится в закрытии возвращенной функции, которую мы определяем, чтобы быть add3. Тот путь, когда мы называем add3, который он знает, где найти, что значение выполняет дополнение.

88
ответ дан Kyle Cronin 2 October 2012 в 15:51
поделиться

Закрытие является функцией, которая может сослаться на состояние в другой функции. Например, в Python, это использует "внутреннее" закрытие:

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
26
ответ дан John Millikin 2 October 2012 в 15:51
поделиться

В нормальной ситуации переменные связываются путем обзора правила: Локальные переменные работают только в определенной функции. Закрытие является способом нарушить это правило временно для удобства.

def n_times(a_thing)
  return lambda{|n| a_thing * n}
end

в вышеупомянутом коде, lambda(|n| a_thing * n} закрытие, потому что a_thing отнесен лямбдой (создатель анонимной функции).

Теперь, если Вы помещаете получающуюся анонимную функцию в функциональную переменную.

foo = n_times(4)

нечто нарушит нормальное правило обзора и начнет использовать 4 внутренне.

foo.call(3)

возвраты 12.

4
ответ дан Eugene Yokota 2 October 2012 в 15:51
поделиться

Чтобы помочь упростить понимание закрытий, могло бы быть полезно исследовать, как они могли бы быть реализованы на процедурном языке. Это объяснение будет следовать за упрощенной реализацией закрытий в Схеме.

Для запуска я должен представить понятие пространства имен. При вводе команды в интерпретатор Схемы это должно оценить различные символы в выражении и получить их значение. Пример:

(define x 3)

(define y 4)

(+ x y) returns 7

определить выражения хранят значение 3 в месте для x и значения 4 в месте для y. Тогда, когда мы звоним (+ x y), интерпретатор ищет значения в пространстве имен и в состоянии выполнить операцию и возвратиться 7.

Однако в Схеме существуют выражения, которые позволяют Вам временно переопределять значение символа. Вот пример:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

то, Что, делает ключевое слово, которому позволяют, начинает новое пространство имен с x как значение 5. Вы заметите, что это все еще в состоянии видеть, что y равняется 4, делая сумму возвращенной, чтобы быть 9. Можно также видеть, что, как только выражение закончилось, x вернулся к тому, чтобы быть 3. В этом смысле x был временно замаскирован локальным значением.

Процедурные и объектно-ориентированные языки имеют подобное понятие. Каждый раз, когда Вы объявляете переменную в функции, которая имеет то же имя как глобальная переменная, Вы получаете тот же эффект.

, Как мы реализовали бы это? Простой путь со связанным списком - голова содержит новое значение, и хвост содержит старое пространство имен. Когда необходимо искать символ, Вы запускаете в голове и прокладываете себе путь вниз хвост.

Теперь позволяют нам пропустить к реализации первоклассных функций в настоящий момент. Более или менее функция является рядом инструкций выполниться, когда функция вызвана, достигнув высшей точки в возвращаемом значении. Когда мы читаем в функции, мы можем сохранить эти инструкции негласно и выполнить их, когда функция вызвана.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

Мы определяем x, чтобы быть 3 и плюс - x, чтобы быть его параметром, y, плюс значение x. Наконец мы звоним плюс - x в среде, где x был замаскирован новым x, этими ценными 5. Если мы просто храним операцию, (+ x y), для функции плюс - x, так как мы находимся в контексте x быть 5, возвращенный результат был бы 9. Это - то, что назвало динамический обзор.

Однако Схема, язык Common LISP и много других языков имеют то, что назвало лексический обзор - в дополнение к хранению операции (+ x y) мы также храним пространство имен в той конкретной точке. Тот путь, когда мы ищем значения, мы видим, что x, в этом контексте, равняется действительно 3. Это - закрытие.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

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

23
ответ дан Kyle Cronin 2 October 2012 в 15:51
поделиться

Простой пример в Groovy для Вашей ссылки:

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1
0
ответ дан 22 November 2019 в 23:38
поделиться

Закрытия предоставляют JavaScript состояние.

состояние в программировании просто означает помнить вещи.

Пример

var a = 0;

a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3

В случае выше, состояние хранится в переменной "a". Мы следуем путем добавления 1 к "a" несколько раз. Мы можем только сделать это, потому что мы можем "помнить" значение. Держатель состояния, "a", держит то значение в памяти.

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

Это, на других языках , обычно выполняется с помощью классов. Класс, точно так же, как переменные, отслеживает свое состояние. И экземпляры того класса, по очереди, также имеют состояние в них. Состояние просто означает информацию, которую можно хранить и получить позже.

Пример

class Bread {
  constructor (weight) {
    this.weight = weight;
  }

  render () {
    return `My weight is ${this.weight}!`;
  }
}

, Как может, мы получаем доступ к "весу" из метода "рендеринга"? Ну, благодаря состоянию. Каждый экземпляр класса Хлеб может представить свой собственный вес путем чтения его из "состояния", места в памяти, где мы могли хранить ту информацию.

Теперь, JavaScript является очень уникальным языком , который исторически не имеет классов (он теперь делает, но под капотом существуют только функции и переменные), таким образом, Закрытия позволяют, чтобы JavaScript помнил вещи, и получите доступ к ним позже.

Пример

var n = 0;
var count = function () {
  n = n + 1;
  return n;
};

count(); // # 1
count(); // # 2
count(); // # 3

пример выше достиг цели "хранения состояния" с переменной. Здорово! Однако это имеет недостаток, что переменная (держатель "состояния") теперь выставляется. Мы можем добиться большего успеха. Мы можем использовать Закрытия.

Пример

var countGenerator = function () {
  var n = 0;
  var count = function () {
    n = n + 1;
    return n;
  };

  return count;
};

var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3

Это фантастически.

Теперь наша функция "количества" может рассчитать. Это только может сделать так, потому что это может "содержать" состояние. Состояние в этом случае является переменной "n". Эта переменная теперь закрывается. Закрытый во времени и пространстве. Вовремя, потому что Вы никогда не будете мочь восстановить его, изменить его, присвоить ему значение или взаимодействовать непосредственно с ним. В пространстве, потому что это географически вкладывается в функции "countGenerator".

, Почему это фантастически? Поскольку, не включая никакой другой сложный и сложный инструмент (например, классы, методы, экземпляры, и т.д.) мы можем к 1. скройте 2. управляйте издалека

, Мы скрываем состояние, переменная "n", который делает это частной переменной! Мы также создали API, который может управлять этой переменной предопределенным способом. В частности, мы можем назвать API как так "количество ()", и это добавляет 1 к "n" от "расстояния". Никоим образом форма или форма любой будет когда-либо мочь получить доступ к "n" кроме через API.

JavaScript действительно удивителен в своей простоте.

Закрытия являются большой частью того, почему это.

0
ответ дан 22 November 2019 в 23:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: