Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.192] |
|
|
Правила раздела:
| Страницы: (495) « Первая ... 441 442 [443] 444 445 ... 494 495 ( Перейти к последнему сообщению ) |
Delphi vs C++ vs C#
, ну и Java немножко, где-то ближе к старшему байту номеров страниц
|
Сообщ.
#6631
,
|
|
|
|
Особо упоротые могут делать так:
![]() ![]() public interface Box<A> { A get(); void set(A a); } public class BoxImpl<A> implements Box<A> { private A value; public BoxImpl (A a) { set(a); } public A get() { return value; } public void set(A a) { value = a; } public String toString() { return value.toString(); } } И юзать везде, где надо Box'ы ![]() ![]() import java.util.Date; interface IntBox extends Box<Integer> {} class IntBoxImpl extends BoxImpl<Integer> implements IntBox { public IntBoxImpl() { super(0); } } public class App { private static void decodeDate(Date date, IntBox year, IntBox month, IntBox day) { year .set(getYear (date)); month.set(getMonth(date)); day .set(getDay (date)); } public static void main(String[] args) { IntBox year = new IntBoxImpl(); IntBox month = new IntBoxImpl(); IntBox day = new IntBoxImpl(); decodeDate(new Date(), year, month, day); System.out.printf("%02d.%02d.%d", day.get(), month.get(), year.get()); } } только я согласен со Смайком, у меня и в делфи-то не возникало необходимости и желания использовать var-параметры, да еще и несколько. А вот вернуть кортеж из нескольких значений (обычно не больше двух) в том же Go или Хаскелле -- вполне нормально и удобно. Добавлено Цитата trainer @ Очевидно в неочевидности. Из вызова функции в Delphi с var/out-аргументом не видно, что аргумент может модифицироваться. P.S. Хотя в Delphi надо просто сразу считать, что вызов функции модифицирует аргументы, если не доказано обратное Вообще достаточно навести мышку на вызов функции и тебе в подсказке отобразится ее сигнатура, ну да ладно... const в делфи полностью константен в соответствием со своим смысом -- передача константной (не-var) ссылки. http://liveworkspace.org/code/16oxEH$16 То же самое и в делфи: ![]() ![]() program Project1; {$APPTYPE CONSOLE} uses SysUtils; type Foo = class value : Integer; public procedure Incr; function Get : Integer; end; procedure Foo.Incr; begin Inc(self.value); end; function Foo.Get : Integer; begin Result := value; end; procedure int_inc(const x : Integer); begin // x := x + 1; // Error: E2064 Left side cannot be assigned to // Inc(x); // Error: E2064 Left side cannot be assigned to end; procedure foo_inc(const f : Foo); begin f.Incr; // f := Foo.Create; f.Free; // Error: E2064 Left side cannot be assigned to end; var x : Integer; var f : Foo; begin f := Foo.Create; int_inc(x); foo_inc(f); WriteLn(f.Get); f.Free; ReadLn; end. ![]() ![]() 1 |
|
Сообщ.
#6632
,
|
|
|
|
Там проблема не в const, а в ссылочной семантике(грубо говоря, ты делаешь константным не объект, а указатель на него, если пользоваться терминологией С++). Для нее "глубокую" константность в стиле С++ не сделаешь. Добавлено Цитата korvin @ То же самое и в делфи Да, но в С++ я могу еще сделать ![]() ![]() void foo(const Foo * const f) // И void foo(const Foo * f) А в Delphi - нет. |
|
Сообщ.
#6633
,
|
|
|
|
Цитата D_KEY @ Да, но в С++ я могу еще сделать ![]() ![]() void foo(const Foo * const f) // И void foo(const Foo * f) А в Delphi - нет. Ради бога, делай. В делфи нельзя неконстантные данные сделать вдруг константными. Хотя во всяких питонах и рубях есть методы а ля freeze. |
|
Сообщ.
#6634
,
|
|
|
|
Цитата korvin @ В делфи нельзя неконстантные данные сделать вдруг константными. Дело не в данных, а контрактах. Я говорю, что не изменю логическое состояние объекта. Цитата Хотя во всяких питонах и рубях есть методы а ля freeze. Костыль, ИМХО. |
|
Сообщ.
#6635
,
|
|
|
|
Цитата D_KEY @ Да, но в С++ я могу еще сделать ![]() ![]() void foo(const Foo * const f) // И void foo(const Foo * f) А в Delphi - нет. Первый вариант, IMHO, бессмысленный, но читал, что некоторые так себя контролируют... |
|
Сообщ.
#6636
,
|
|
|
|
Цитата MyNameIsIgor @ Первый вариант, IMHO, бессмысленный В случае передачи параметров - да. Цитата но читал, что некоторые так себя контролируют... В данном случае проще воспользоваться ссылками Или ты вообще о константности передаваемых по значению аргументов? |
|
Сообщ.
#6637
,
|
|
|
|
Цитата D_KEY @ Дело не в данных, а контрактах. Я говорю, что не изменю логическое состояние объекта. Какая разница? Ты не изменишь -- кто-то другой изменит. Надежней делать иммутабельные объекты/структуры с нужными свойствами или копии для таких случаев. Кроме того это несколько ухудшает модульность: если мне вдруг захотелось, чтобы немутирующий метод в моем классе вдруг стал мутирующим, то это поломает весь код, который его использует. То же самое и с IO в Хаскелле, только еще хуже. |
|
Сообщ.
#6638
,
|
|
|
|
Цитата korvin @ Какая разница? Ты не изменишь -- кто-то другой изменит. И в чем проблема? Изменения - естественная штука в ОО-мире. Цитата Надежней делать иммутабельные объекты/структуры с нужными свойствами или копии для таких случаев. Тебе никто не мешает это делать, если нужно Вопрос в том, как мне написать функцию, принимающую объект с гарантией того, что я не буду дергать методы, изменяющие его состояние? Сам объект при этом может быть изменяемым. Можно делать это через интерфейсы/абстрактные классы, но часто это является просто введением лишних сущностей, да и заставляет использовать полиморфизм на ровном месте. Цитата если мне вдруг захотелось, чтобы немутирующий метод в моем классе вдруг стал мутирующим, то это поломает весь код, который его использует. Нет. Это не поломает весь код. Но будет означать, что ты плохо спроектировал что-либо. Если ты о кэшировании и т.п., то для этого есть mutable, позволяющий менять "физическое" состояние без изменения логического. |
|
Сообщ.
#6639
,
|
|
|
|
Цитата D_KEY @ В яве скоро будут(уже есть?) лямбды. Так что тоже можно будет фактически создавать локальные функции Так с помощью анонимных классов можно уже сейчас это делать. Но главное отличие в том, что все джава-иде такие классы нормально показывают. |
|
Сообщ.
#6640
,
|
|
|
|
Цитата MyNameIsIgor @ Первый вариант, IMHO, бессмысленный, но читал, что некоторые так себя контролируют... Я какое-то время так делал. Потом забил, ибо решил, что бессмысленно |
|
Сообщ.
#6641
,
|
|
|
|
Цитата D_KEY @ И в чем проблема? Изменения - естественная штука в ОО-мире. И в чем тогда смысл явного указания в функции, что она не меняет состояние объекта? Цитата D_KEY @ Тебе никто не мешает это делать, если нужно Какие тогда претензии к Делфи? =) Цитата D_KEY @ Вопрос в том, как мне написать функцию, принимающую объект с гарантией того, что я не буду дергать методы, изменяющие его состояние? Сам объект при этом может быть изменяемым. Так и не надо писать такие функции. ;-) Зачем она тебе? Зачем она кому-то другому? Цитата D_KEY @ Можно делать это через интерфейсы/абстрактные классы, но часто это является просто введением лишних сущностей Да, гораздо лучше взять одно слово const и позволить пихать его чуть ли не везде, при этом каждый раз это имеет какой-то свой эффект. Как там было у Пайка или кого там про Foo ![]() ![]() foo::Foo *myFoo = new foo::Foo(foo::FOO_INIT); Теперь еще и ![]() ![]() const Foo * copy(const Foo * const foo) const; =) Цитата D_KEY @ да и заставляет использовать полиморфизм на ровном месте. А что плохого в полиморфизме? =) Цитата D_KEY @ Нет. Это не поломает весь код. Но будет означать, что ты плохо спроектировал что-либо. Сфига ли? Реализация метода -- это мое дело. Если я изначально методом ничего не мутирую и повешу на него const, то я резко ограничу возможность будущей модификации этого метода. Цитата D_KEY @ Если ты о кэшировании и т.п., то для этого есть mutable, позволяющий менять "физическое" состояние без изменения логического. Цитата Костыль, ИМХО. =) Добавлено Цитата [S]mike @ Цитата D_KEY @ В яве скоро будут(уже есть?) лямбды. Так что тоже можно будет фактически создавать локальные функции Так с помощью анонимных классов можно уже сейчас это делать. Но главное отличие в том, что все джава-иде такие классы нормально показывают. Не, анонимные классы -- это куча лишнего мусора. Когда нужен всего-лишь один метод, то лямбды намного удобней и читабельеней: ![]() ![]() button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { model.doSomething(); } }); ![]() ![]() button.actionListeners += (ActionEvent e) => { model.doSomething() }; А когда нужно (пере)определить несколько, то гораздо удобней и читабельней создать отдельный класс. Так что ждем восьмую джаву с лямбдами или юзаем нормальные jvm-языки =) |
|
Сообщ.
#6642
,
|
|
|
|
Цитата korvin @ Вообще достаточно навести мышку на вызов функции и тебе в подсказке отобразится ее сигнатура, ну да ладно... не ладно, возможность изменения входных параметров - это критично и должно быть видно безо всяких движений мыши. Цитата D_KEY @ В С++ нет out/var параметров. Если ты имеешь в виду ссылки/указатели - то это отдельные типы данных. только смысла это не меняет, так как можно неявно преобразовать значение в ссылку. если бы было только явное преборазование ( что нить типа void foo(int & i) {} int j; foo((auto&)j); тогда явно было бы видно, что параметр меняется. Но синтаксис конечно убог. Впрочем для с++ нутых - сойдет :-D А по поводу константности в дельфе: ![]() ![]() type TRec = record Arr: array[0..10] of Integer; // чтоб оптимизатор не мутил воду Value: Integer; procedure Inc; end; procedure TRec.Inc; begin Value := Value + 1; end; procedure Foo(const R: TRec); begin R.Inc; end; procedure TForm11.FormCreate(Sender: TObject); var R: TRec; I: Integer; begin for I := Low(R.Arr) to High(R.Arr) do R.Arr[I] := I; // чтоб оптимизатор не мутил воду R.Value := 1; Foo(R); if R.Value <> 1 then raise Exception.Create('ХА-ХА-ХА'); // Злобный смех супер-злодея end; |
|
Сообщ.
#6643
,
|
|
|
|
Цитата korvin @ Цитата D_KEY @ И в чем проблема? Изменения - естественная штука в ОО-мире. И в чем тогда смысл явного указания в функции, что она не меняет состояние объекта? В этом и смысл - указать, что мы не будем изменять состояние Это делает код более понятным, избавляет от ошибок и пр.Цитата Какие тогда претензии к Делфи? =) Не к делфи, а к языкам с параллельным существованием двух семантик - ссылочной и значений, которые определяются неявным образом и зависят от "вида" типа(класс/структура/встроенный тип и пр.) Цитата Так и не надо писать такие функции. ;-) Зачем она тебе? Зачем она кому-то другому? Цитата Да, гораздо лучше взять одно слово const и позволить пихать его чуть ли не везде, при этом каждый раз это имеет какой-то свой эффект. Один и тот же Цитата Как там было у Пайка или кого там про Foo ![]() ![]() foo::Foo *myFoo = new foo::Foo(foo::FOO_INIT); Теперь еще и ![]() ![]() const Foo * copy(const Foo * const foo) const; =) Ничего не понял. Цитата А что плохого в полиморфизме? =) Ничего. Но здесь он не нужен и используется для решения совсем несвязанной с ним проблемы логической константности. Цитата Сфига ли? Реализация метода -- это мое дело. Если я изначально методом ничего не мутирую и повешу на него const, то я резко ограничу возможность будущей модификации этого метода. Ничего ты не ограничишь. const или не const - это не твое дело, это декларация наличия/отсутствия логического изменения состояния. Цитата Цитата D_KEY @ Если ты о кэшировании и т.п., то для этого есть mutable, позволяющий менять "физическое" состояние без изменения логического. Цитата Костыль, ИМХО. =) В чем костыльность-то? Хочешь изменять объект, не изменяя логического состояния - делай это явно и для определенных полей. Добавлено Цитата jack128 @ только смысла это не меняет, так как можно неявно преобразовать значение в ссылку. если бы было только явное преборазование ( что нить типа void foo(int & i) {} int j; foo((auto&)j); тогда явно было бы видно, что параметр меняется. Если это действительно нужно, то: ![]() ![]() void foo(int *const i) {} // ... int j; foo(&j); |
|
Сообщ.
#6644
,
|
|
|
|
Цитата jack128 @ только смысла это не меняет, так как можно неявно преобразовать значение в ссылку. если бы было только явное преборазование ( Как бы тогда работали операторы и их переопределение? |
|
Сообщ.
#6645
,
|
|
|
|
korvin, вот тебе кусочек нашего вектора:
![]() ![]() // не меняем передаваемый нам объект(т.к. копируем его), меняем свое состояние void push_back (const value_type& val); // удаляем у себя объект с конца, меняем свое состояние void pop_back(); // возвращаем неизменяемую ссылку на наш последний элемент // (позволительно и для константного объекта, т.к. клиент не изменит наши данные) const_reference back() const; // возвращаем изменяемую ссылку на наш последний элемент, // (возможно только для неконстантного объекта) reference back(); Может так будет понятнее... |