Дизайн языка такой.
Смотри,
1) Сборщика мусора нет.
2) Все значения С/С++ value_type.
Большинство контейнеров это именно контейнеры значений, — такой подход более универсален, требует меньше накладных расходов и заметно более производителен (выделение в куче достаточно дорогая операция).
Контейнеры массива или стека обычно выделяют большой кусок памяти и в нём последовательно размещают объекты. т.е. delete stack.pop() бессмысленная запись, т.к. память под удалённый элемент никогда не выделялась, он находился в заранее выделенном большом куске.
Можно написать такой стек как вы хотите, на основе стека указателей:
class deque_witch_bj_and_hookers
{
void push( int v ) { m_impl.push( new int(v) ); }
int* pop( int v ) { int* r = m_impl.top(); m_impl.pop(); return r; }
std::deque<int *> m_impl;
};
— Но, если для него вызвать stack.pop() будет утечка памяти, никто за вас delete не вызовет. Тут на помощь приходят умные указатели, но это другая длинная история. Главное что такие красивые ништяки не даются бесплатно.
>> Я не очень знаю javaScript, но разве нельзя сделать так:…
>Можно
Ага! Т.е. массив стал чуточку менее массивом. Согласны?
>> Какие гарантии по вашему исчезают, если всместо int передавать Point например?
>Исчезает гарантия, что объект будет себя вести так, как мы ожидаем.
Я не знаю чего вы ожидаете, конечно, но, если, мы говорим о C/C++, то все невиртуальные методы будут вести себя так-же как определены в классе Point.
Если мы говорим о Java, где все методы виртуальные (если не ошибаюсь), мы всё равно получаем известный интерфейс, который должен быть подвидом Point.
Т.е., когда я пишу код на Java, я могу быть уверен что правильно использую объект, потому что у меня есть формально описанный интерфейс к нему.
Если кто-то другой создал наследника Point, то я могу быть уверенным что он пытался сделать подвид точки а не класс считающий количество «X» и «Y» хромосом.
Почему? Да потому что он прямо так и написал:
class ColorPoint extends Point {},
— Круто да? Человек ясно написал что создал подвид точки, но знаю об этом не только я но и компилятор, который ругнётся если реализация совсем уж косячная.
По-моему намного круче чем:
function ColorPoint()… // мамой клянусь, это подвид класса Point
{
x: 0, // x,y должны быть целыми числами, если
y: 0, // какой-то му--ла запишет сюда что-то ещё
// я не поленюсь, найду где ты живёшь
// и засуну глобус в место которым ты думаешь
clr: "" // строка вида "#ffffff". Честно.
}
PS: я знаю что JS можно сделать приватные переменные и написать интерфейсные методы, но в этом случае ошибка будет выявлена только при исполнении (т.е. не понятно когда).
В java не так, там есть не просто набор методов, а интерфейс. Все производные это реализации интерфейса. Т.е. мы всегда знаем минимальный набор методов элемента, их возвращаемые и принимаемые параметры. С параметрами в JS вообще туго кстати.
К примеру, Java
interface Car
{
int x();
int y();
…
}
interface Plane
{
int x();
int y();
int z();
…
}
// есть такая функция
int distance( Car c1, Car c2 ) { return… }
MyCar car1, car2;
MyPlane plane1, plane2;
int dst1 = distance( car1, car2 ); // ok
int dst1 = distance( car1, plane1 ); // compile error
int dst1 = distance( plane1, plane1 ); // compile error
Допустим в JS есть функция аналогичная distance(), когда вы её писали даже не подозревали что существует класс Plane, тогда:
int dst1 = distance( car1, car2 ); // ok
var dst2 = distance( car1, plane1 ); // неверное значение
var dst3 = distance( plane1, plane1 ); // неверное значение
Наследник, по определению, является разновидностью базового класса. Если это не так, то это явная ошибка проектирования. Т.е. если для класса А верно что использовать его можно так:
A a;
a.init();
a.do_something();
a.deinit();
То это верно и для наследника «B».
Например здесь функция foo() гарантировано отработает правильно:
Java:void utilize_object( A o ) { o.init(); i.do_something(); o.deinit(); }
utilize_object( new A );
utilize_object( new B ); // B extends A
В JavaScript нет, т.к. объект имеющий все нужные методы, может подразумевать совсем другие соглашения по работе с ним, например, необходимо вызвать метод init_external() до init().
> Да и вы сейчас нарушаете принцип инкапсуляции?
Не понял о чём вы.
> Изменяется сразу. Потому что, цитирую сам себя: «в JS сторона точно так же имеет гарантию на объект. То, что в String добавили какой-нибудь метод, не делает String менее String'ом»
Я не очень знаю javaScript, но разве нельзя сделать так:
Array.prototype.pop = null;
или
Array.prototype.pop = function() {}
И, потом, я правда не понимаю как это «Изменяется сразу»?
Какие гарантии по вашему исчезают, если всместо int передавать Point например?
Миксины меняют класс/метод на этапе компиляции, а не исполнения. Т.е. мы можем коротко создать определение нового класса или подмешать в определение нужные нам методы.
Может мы говорим о каких-то разных миксинах? Я имею ввиду те, которые есть, например в «D»: d.digitalmars.com/2.0/mixin.html
И что выполнит ваша doSomething() из javascript? В Java я могу сказать посмотрев в объявление класса, в javascript мне нужно исследовать всю программу.
Если, вместо int, передать структуру/класс ничего не изменится.
Использование mixin'ов тоже выдаст ошибку компиляции при некорректных параметрах/окружении.
> В общем случае сторона, использующая объект, не имеет на него гарантий ни в одном языке программирования вообще.
Да ладно! например написав на C такое: int foo( int param ) {}
я уверен что param это целое число, и оно передано. Не надо проверять его на defined и прочее.
Иногда дело доходит до mixin'ов, шаблонов и прочего, но если им передать что-то не то, компилятор укажет на их неверное использование в конкретном месте.
Например: trmplate T square( T v ) { return v * v; }
Только если возвращать по ссылке, но по ссылке возвращать нельзя потому что дальнейшее изменение массива может изменить значение на которое указывает ссылка.
В яваскрипте такое возможно, в частности, потому что массив там это не массив элементов, а массив ссылок.
В яваскрпте все объекты передаются по ссылке, деструкторов нет. Соответственно можно отцепить элемент от контейнера и вернуть ссылку на него.
Мизерная проблема потери производительности из за того что возвращается ссылка, даже если она не нужна, в скриптовых языках традиционно игнорируется.
Если в конструкторе бросилось исключение, то копия не создается, контейнер не меняется, дальше обычная раскрутка стека. Что страшного?
И вашей цитаты: «Return by value, however, is inefficient: it involves at least one redundant copy constructor call.»
Или мы с вами разные вещи называем производительностью?
Pop переводится как «выдавить», так что метод с таким именем вполне может возвращать удалённый элемент. Понятно что в плюсах, из соображений производительности метод pop() ничего не возвращает.
По моему скромному мнению, это скорее зло потому как сторона использующая объект ни имеет на него никаких гарантий (пока кучу if'ов не исполнит конечно). Что касается приведённого вами примера, то задача намного лучше решается наследованием.
Я вообще за popBack() если что.
Хотя если выбирать между двумя костяными названиями, то дартовский мне нравится больше, тем, что хотя бы понятно что произойдёт с исходным массивом. Что вернёт метод не очевидно ни в том ни в другом случае.
По поводу селекторов не знаю. Что касается первого замечания, вполне логично что создание элемента не зависит от того к какому документу он в последствии будет прикреплён.
Да, имеет смысл:
во первых по тому что компактные исходники быстрее за грузятся,
Во вторых потому что в JavaScript нет статической типизации и написать IDE которая будет делать автоподстановку как минимум затруднительно.
Но, смысл метода pop() не очевиден, а привычен, вы так же могли заучить что воображаемый метод delete() удаляет первый элемент. Во вторых в Дарте есть статическая типизация.
Смотри,
1) Сборщика мусора нет.
2) Все значения С/С++ value_type.
Большинство контейнеров это именно контейнеры значений, — такой подход более универсален, требует меньше накладных расходов и заметно более производителен (выделение в куче достаточно дорогая операция).
Контейнеры массива или стека обычно выделяют большой кусок памяти и в нём последовательно размещают объекты. т.е. delete stack.pop() бессмысленная запись, т.к. память под удалённый элемент никогда не выделялась, он находился в заранее выделенном большом куске.
Можно написать такой стек как вы хотите, на основе стека указателей:
class deque_witch_bj_and_hookers
{
void push( int v ) { m_impl.push( new int(v) ); }
int* pop( int v ) { int* r = m_impl.top(); m_impl.pop(); return r; }
std::deque<int *> m_impl;
};
— Но, если для него вызвать stack.pop() будет утечка памяти, никто за вас delete не вызовет. Тут на помощь приходят умные указатели, но это другая длинная история. Главное что такие красивые ништяки не даются бесплатно.
>Можно
Ага! Т.е. массив стал чуточку менее массивом. Согласны?
>> Какие гарантии по вашему исчезают, если всместо int передавать Point например?
>Исчезает гарантия, что объект будет себя вести так, как мы ожидаем.
Я не знаю чего вы ожидаете, конечно, но, если, мы говорим о C/C++, то все невиртуальные методы будут вести себя так-же как определены в классе Point.
Если мы говорим о Java, где все методы виртуальные (если не ошибаюсь), мы всё равно получаем известный интерфейс, который должен быть подвидом Point.
Т.е., когда я пишу код на Java, я могу быть уверен что правильно использую объект, потому что у меня есть формально описанный интерфейс к нему.
Если кто-то другой создал наследника Point, то я могу быть уверенным что он пытался сделать подвид точки а не класс считающий количество «X» и «Y» хромосом.
Почему? Да потому что он прямо так и написал:
class ColorPoint extends Point {},
— Круто да? Человек ясно написал что создал подвид точки, но знаю об этом не только я но и компилятор, который ругнётся если реализация совсем уж косячная.
По-моему намного круче чем:
function ColorPoint()… // мамой клянусь, это подвид класса Point
{
x: 0, // x,y должны быть целыми числами, если
y: 0, // какой-то му--ла запишет сюда что-то ещё
// я не поленюсь, найду где ты живёшь
// и засуну глобус в место которым ты думаешь
clr: "" // строка вида "#ffffff". Честно.
}
PS: я знаю что JS можно сделать приватные переменные и написать интерфейсные методы, но в этом случае ошибка будет выявлена только при исполнении (т.е. не понятно когда).
К примеру, Java
interface Car
{
int x();
int y();
…
}
interface Plane
{
int x();
int y();
int z();
…
}
// есть такая функция
int distance( Car c1, Car c2 ) { return… }
MyCar car1, car2;
MyPlane plane1, plane2;
int dst1 = distance( car1, car2 ); // ok
int dst1 = distance( car1, plane1 ); // compile error
int dst1 = distance( plane1, plane1 ); // compile error
Допустим в JS есть функция аналогичная distance(), когда вы её писали даже не подозревали что существует класс Plane, тогда:
int dst1 = distance( car1, car2 ); // ok
var dst2 = distance( car1, plane1 ); // неверное значение
var dst3 = distance( plane1, plane1 ); // неверное значение
A a;
a.init();
a.do_something();
a.deinit();
То это верно и для наследника «B».
Например здесь функция foo() гарантировано отработает правильно:
Java:
void utilize_object( A o ) { o.init(); i.do_something(); o.deinit(); }
utilize_object( new A );
utilize_object( new B ); // B extends A
В JavaScript нет, т.к. объект имеющий все нужные методы, может подразумевать совсем другие соглашения по работе с ним, например, необходимо вызвать метод init_external() до init().
> Да и вы сейчас нарушаете принцип инкапсуляции?
Не понял о чём вы.
Я не очень знаю javaScript, но разве нельзя сделать так:
Array.prototype.pop = null;
или
Array.prototype.pop = function() {}
И, потом, я правда не понимаю как это «Изменяется сразу»?
Какие гарантии по вашему исчезают, если всместо int передавать Point например?
Миксины меняют класс/метод на этапе компиляции, а не исполнения. Т.е. мы можем коротко создать определение нового класса или подмешать в определение нужные нам методы.
Может мы говорим о каких-то разных миксинах? Я имею ввиду те, которые есть, например в «D»:
d.digitalmars.com/2.0/mixin.html
Использование mixin'ов тоже выдаст ошибку компиляции при некорректных параметрах/окружении.
Вы странные утверждения здесь приводите.
В примере «object» это ссылка на объет типа MyObject либо null.
Анологичная сигнатура в JS значит: «object» или не передан, либо null, либо что угодно.
Да ладно! например написав на C такое:
int foo( int param ) {}
я уверен что param это целое число, и оно передано. Не надо проверять его на defined и прочее.
Иногда дело доходит до mixin'ов, шаблонов и прочего, но если им передать что-то не то, компилятор укажет на их неверное использование в конкретном месте.
Например:
trmplate T square( T v ) { return v * v; }
double v1 = square( 5 ); // 25
const char* v2 = square( "five" ); // compile error
Т.е. метод возвращает что-то разумное или программа не запускается. Разница очень есть.
В яваскрипте такое возможно, в частности, потому что массив там это не массив элементов, а массив ссылок.
Мизерная проблема потери производительности из за того что возвращается ссылка, даже если она не нужна, в скриптовых языках традиционно игнорируется.
Если в конструкторе бросилось исключение, то копия не создается, контейнер не меняется, дальше обычная раскрутка стека. Что страшного?
Или мы с вами разные вещи называем производительностью?
Хотя если выбирать между двумя костяными названиями, то дартовский мне нравится больше, тем, что хотя бы понятно что произойдёт с исходным массивом. Что вернёт метод не очевидно ни в том ни в другом случае.
во первых по тому что компактные исходники быстрее за грузятся,
Во вторых потому что в JavaScript нет статической типизации и написать IDE которая будет делать автоподстановку как минимум затруднительно.
Но, смысл метода pop() не очевиден, а привычен, вы так же могли заучить что воображаемый метод delete() удаляет первый элемент. Во вторых в Дарте есть статическая типизация.