03.08.2020, 21:03 [+3 UTC]
в нашей команде: 4 686 чел. | участники онлайн: 1 (рекорд: 21)

:: РЕГИСТРАЦИЯ

задать вопрос

все разделы

правила

новости

участники

доска почёта

форум

блоги

поиск

статистика

наш журнал

наши встречи

наша галерея

отзывы о нас

поддержка

руководство

Версия системы:
7.89 (25.04.2020)
JS-v.1.45 | CSS-v.3.39

Общие новости:
13.04.2020, 00:02

Форум:
02.08.2020, 11:21

Последний вопрос:
02.08.2020, 22:48
Всего: 152762

Последний ответ:
02.08.2020, 16:59
Всего: 260347

Последняя рассылка:
03.08.2020, 16:45

Писем в очереди:
0

Мы в соцсетях:

Наша кнопка:

RFpro.ru - здесь вам помогут!

Отзывы о нас:
08.01.2012, 17:30 »
Данилов Артем Владимирович
Большое спасибо за ответ, думаю ваш совет мне поможет. [вопрос № 185115, ответ № 269497]
14.02.2020, 17:19 »
dar777
Это самый лучший ответ!!! [вопрос № 197726, ответ № 279459]

РАЗДЕЛ • Pascal / Delphi / Lazarus

Создание программ на языках Pascal, Delphi и Lazarus.

[администратор рассылки: Зенченко Константин Николаевич (Старший модератор)]

Лучшие эксперты в этом разделе

Зенченко Константин Николаевич
Статус: Старший модератор
Рейтинг: 341
puporev
Статус: Профессор
Рейтинг: 37
Gluck
Статус: 1-й класс
Рейтинг: 1

Перейти к консультации №:
 

Консультация онлайн # 144768
Раздел: • Pascal / Delphi / Lazarus
Автор вопроса: Maksim Trofimov
Отправлена: 23.09.2008, 15:33
Поступило ответов: 3

hi...

Есть ли в delphi хороший и удобный способ создания динамического массива?
Перед мной стала задача создать динамический массив компонентов TEdit.
Полистав немного справку, на меня уставилась функция SetLength() и вот что из этого вышло:

// очистка всех полей редактирования переданной формы от текста
procedure clear_ed(form: TForm; eds_number: integer);
var
eds: array of TEdit;
i: integer;
begin
SetLength(eds, eds_number);
for i := 0 to form.Componentcount-1 do
begin
if(form.components[i] is TEdit) then
begin
eds[i]:= TEdit(form.components[i]);
eds[i].Clear;
end;
end;
end;

Однако, при повторном вызове этой функции, например кнопкой какой-нибудь - вылетает деббагер (
Еще немного полистав справку, нашелся GetMem(), от которого толку еще меньше...
Неужели нет в delphi такой юзабильной функции как например в С++(new) или в С(malloc, calloc) ?
Или что можно сделать с кодом выше?

спасибо...

Состояние: Консультация закрыта

Ответ # 230026 от Кэр Лаэда

Здравствуйте, Maksim Trofimov!


а точно у вас она ругается только при повторном вызове ? посмотрите сами
вы перебираете все компоненты на форме

for i := 0 to form.Componentcount-1 do

и если компонент is TEdit то вы что то делаете с компонентом т.е. ссылаетесь на какой то объект в массиве eds

и догадайтесь что происходит когда у вас на форме кроме TEdit есть например кнопка, следовательно вы выходите за диапазон массива eds.

вам нужно делать так, так как у вас изначально известно что в массиве eds хранятся ссылки только на TEdit, значит вам не нужно перебирать все компоненты на форме а обращаться непосредственно к элементам массива .

for i := 0 to length(eds)-1 do
begin
eds[i].Clear;
end;



Консультировал: Кэр Лаэда
Дата отправки: 23.09.2008, 15:45

Рейтинг ответа:

0

[подробно]

Сообщение
модераторам

Отправлять сообщения
модераторам могут
только участники портала.
ВОЙТИ НА ПОРТАЛ »
регистрация »

Ответ # 230027 от Лукьяненко Алексей Валериевич

Здравствуйте, Maksim Trofimov!

1) у вас дебаггер вылетает из-за неправильного использования массива.
Вы выделяете eds_number элементов, а обращаетесь (записываетев массив) по индексу компонента. Вот и получается, что если Вы выделили место под 1 элемент, а на форма у Вас 3 компонента - то будет ошибка - обращение к несуществующему элементу при I = 2 к примеру. Индексы обращения к разным массивам должны быть разными.
Правильно будет так:

var
eds: array of TEdit;
i, j: integer;
begin
SetLength(eds, eds_number);
J := 0;
for i := 0 to form.Componentcount-1 do
begin
if form.components[i] is TEdit then
begin
eds[j]:= TEdit(form.components[i]);
eds[j].Clear;
Inc(j);
end;
end;
end;

2) От GetMem толк есть. Если использовать тип PPointerArray (для Вашего примера):

var
eds: PPointerArray;
i, j: integer;
begin
eds := GetMem(eds_number * sizeof(TEdit))
J := 0;
for i := 0 to form.Componentcount-1 do
begin
if form.components[i] is TEdit then
begin
eds^[j]:= form.components[i];
TEdit(eds^[j]).Clear;
Inc(j);
end;
end;
end;


Консультировал: Лукьяненко Алексей Валериевич
Дата отправки: 23.09.2008, 15:46

Рейтинг ответа:

0

[подробно]

Сообщение
модераторам

Отправлять сообщения
модераторам могут
только участники портала.
ВОЙТИ НА ПОРТАЛ »
регистрация »

Ответ # 230034 от Виктор Пырлик

Здравствуйте, Maksim Trofimov!
Судя по названию — функция для очистки TEdit компонент..
Но тогда, массив создавать не к чему.
Вот так, это может быть:

 procedure  clear_ed(form: TForm);
 var   i: integer;
 begin 
    for  i := 0  to  form.Componentcount-1  do 
   begin 
      if (form.components[i] is TEdit)  then 
      (form.components[i] as TEdit).Clear;
   end ;
 end ;
 {$R *.dfm} 
 procedure  TForm1.Button1Click(Sender: TObject);
 begin 
   clear_ed(self);
 end ;


Во вторых – создание динамических массивов в Delphi подчиняется темже принципам что и в C/C++. Но вы работаете не с простыми типами а с классами.
TEdit – это класс, потомок, далеко не первый, в дереве объектов.
И для его инициализации надо использовать метод Create.
Вот пример (в приложении) обоих процедур:

Приложение:


Консультировал: Виктор Пырлик
Дата отправки: 23.09.2008, 17:00

Рейтинг ответа:

0

[подробно]

Сообщение
модераторам

Отправлять сообщения
модераторам могут
только участники портала.
ВОЙТИ НА ПОРТАЛ »
регистрация »

Мини-форум консультации № 144768

Вадим Исаев ака sir Henry
Мастер-Эксперт

ID: 425

# 1

= общий = | 23.09.2008, 16:18 | цитировать цитировать  | профиль профиль  |  отправить письмо в личную почту пейджер

Maksim Trofimov
А на сколько Вы уверены, что eds_number у Вас совпадает с количеством Edit'ов на форме?
Если Вы заранее не знаете количество, то динамический массив не самый удобный элемент. Проще использовать какой-либо класс эмулирующий динамический массив. Например TList:

Var
  List: TList;
  i: integer;
Begin
  List:=TList.Create;
  for i := 0 to form.Componentcount-1 do
    if(form.components[i] is TEdit) then
     List.Add(form.components[i]);
End;

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Gladiator

# 2

= общий = | 23.09.2008, 16:26

А зачем нужно повторно обращаться к функции, где устанавливается размер динамического массива?
Если в процессе работы программы кол-во эдитов остаётся неизменным, тогда выполните один раз SetLength и всё.
При уменьшении или увеличении числа эдитов желательно обнулить размер массива SetLength(eds, 0) и заново заполнить его, но не
вижу смысла использовать здесь массив.

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Виктор Пырлик

# 3

= общий = | 23.09.2008, 17:08

Судя по тому, что делает процедура - городить массив не к чему.
Просто, есть попытка "прикрутить" подход C/C++ к Delphi, но, как я уже говорил, далеко не всегда и в СИ срабатывают конструкторы по умолчанию, они могут быть и не определены (например).. Даже там (С/С++) new, malloc и т.д. Вы выделяете память под размер структур (классов) но не инициализируете их. Хотя, это рассылка по Delphi, потому, разговор о других языках не уместен.

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 4

= общий = | 24.09.2008, 01:49

Все ясно, но разъесните мне пожайлуста, в обоих случаях(массив по setlength и getmem) мы получаем массив указателей, а для List? Получается, если мы получаем массив указателей, то удалять элементы нельзя вот так: eds[i].destroy(); ? Но сам массив удалить то можно, вот так freemem(eds), но это для getmem(), а для setlength() ? Хотелось бы разобраться с памятью в delphi, а то что в моей задачи не нужно массива, это просто пример )

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Вадим Исаев ака sir Henry
Мастер-Эксперт

ID: 425

# 5

= общий = | 24.09.2008, 05:39 | цитировать цитировать  | профиль профиль  |  отправить письмо в личную почту пейджер

Maksim Trofimov
Теория.
TList это тоже массив указателей, только более современного и удобного вида, оформленный как класс. Если тип класса в этом массиве заранее неизвестен, то его надо приводить к определённому типу.
Destroy() - этот метод используется самим классом, Вам вместо него нужно вызывать универсальный метод Free чтобы удалить элемент.
Практика.
Если Вы собираетесь делать массив компонентов, а не вообще всего на свете, то для этого есть специальный класс - TComponentList. При его использовании, для манипуляции с компонентами которые в нём хранятся, делать приведение типов уже необязательно, т.к. по умолчанию предполагается, что там хранятся именно TComponent и его наследники:

Var
  List: TComponentList;
  i: integer;
Begin
  List:=TComponentList.Create;
  for i := 0 to form.Componentcount-1 do
    if(form.components[i] is TEdit) then
     List.Add(form.components[i]);
  //А вот и изюминка
  for i:=0 To List.Count-1 Do
    List[i].Clear;
  //Как только List становится ненужен - удаляем его
  //При этом автоматически вызывается деструктор хранимых в List компонентов
  List.Free;
End; 

На сегодняшний день использование динамических массивов уже во многом устаревшая практика и использовать его, по сравнению с классом, неудобно. Например удалив динамический массив, Вы оставляете в памяти объекты, которые он хранил и полностью теряете связь с этими объектами. Вам это надо? А программе, которая потеряла большой объём памяти и уже не может до него добраться?

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Виктор Пырлик

# 6

= общий = | 24.09.2008, 10:11

© Цитата:
Все ясно, но разъесните мне пожайлуста, в обоих случаях(массив по setlength и getmem) мы получаем массив указателей

Нет. "массив указателей", это нечто иное. таким образом, мы выделяем память размером равную структуре объекта (не важно, какой язык, класс, запись, структура, тип определенный пользователем и т.д.) умноженную на количество этих элементов (если это массив).
Инициализация же подразумевает заполнение внутренней структуры объекта (конкретного, т.е. экземпляра/элемента массива) определенными значениями. Потому, понятие выделить память и инициализировать объект есть не одно и то же.
Например, struct { int x; float y; char *s;} имеет размер ~ 12 байт.. Но, попробуйте использовать эту структуру без инициализации.. x, y будет содержать мусор, а s вообще будет ссыласться неизвестно на что.. таким образом:

 typedef struct {
 int x;
 float y;
 char *s;
} _MSTR;

_MSTR *m = (_MSTR*) malloc(1);
printf("%i\t%f\t%s",m->x,m->y,m->s);


вызовет ошибку – не определен указатель s
а значения x и y будут иметь
x = 13788852
y = 6,1040561105989E-42
т.е. мусор.

Теперь, Вы поняли чем отличается "выделить память" и "инициализировать" ? И для чего нужны конструкторы как в C/C++ так и в Delphi ?
Создавая массив экземпляров классов (а почти все есть классы, за исключением простых типов), надо использовать механизмы работы с классами и методы самих классов.

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 7

= общий = | 24.09.2008, 11:40

sir Henry

Все отлично.., но если мне не нужно будет очищать список компонентов в текущем блоке и компоненты должны существовать до конца выполнения программы? То есть, мне обязательно надо вызвать метод free() перед завершением onclose() или это за меня сделает ось?

© Цитата:
Например удалив динамический массив, Вы оставляете в памяти объекты, которые он хранил и полностью теряете связь с этими объектами

Вы имели ввиду: удалив указатель на массив, мы теряем доступ к объектам в памяти?

© Цитата:
На сегодняшний день использование динамических массивов уже во многом устаревшая практика и использовать его, по сравнению с классом, неудобно.

Удобно то удобно, но а если нужно будет высокое бистродействие программы? Тут то на помощь придут дедовские массивы ))

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Вадим Исаев ака sir Henry
Мастер-Эксперт

ID: 425

# 8

= общий = | 24.09.2008, 12:11 | цитировать цитировать  | профиль профиль  |  отправить письмо в личную почту пейджер

1. Не ОСь, а сама программа при своём завершении. Сделает.
Вопрос в другом. Как только Вы вышли из процедуры, Вы автоматически теряете доступ к списку компонентов вне зависимости от того, с помощью чего этот список составлен - класса или динамического массива.
На самом деле этот вопрос некорректный, т.к. Вы не пояснили, какой список Вы имеете в виду - если список Form.Components, то он как был, так и остался, а вот список внутри процедуры Вы однозначно теряете навсегда.
2. Да.
3. А Вы протестируйте. Измерьте время выполнения. smile

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 9

= общий = | 24.09.2008, 12:44

sir Henry

© Цитата:
если список Form.Components, то он как был, так и остался

Не, ну это понятно )

© Цитата:
Как только Вы вышли из процедуры, Вы автоматически теряете доступ к списку компонентов вне зависимости от того, с помощью чего этот список составлен - класса или динамического массива.

Я вот о чем... получается, что каждый раз, когда вызывается функция очистки полей edit (на нашем примере), список компонентов (list) составляется занова, при этом он очищается программой автоматически, да?

© Цитата:
А Вы протестируйте. Измерьте время выполнения.

Видимо, я еще не готов. Вот когда стану суровым программистом... ))
А пока...лень матушка )

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 10

= общий = | 24.09.2008, 12:54

Виктор Пырлик

© Цитата:
Теперь, Вы поняли чем отличается "выделить память" и "инициализировать" ? И для чего нужны конструкторы как в C/C++ так и в Delphi ?

Это то я понял... Я вот узнать хотел что за звери такие Initialize(); Finalize(); и есть ли функция очистки, того что setlength() выделила?

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Вадим Исаев ака sir Henry
Мастер-Эксперт

ID: 425

# 11

= общий = | 24.09.2008, 13:40 | цитировать цитировать  | профиль профиль  |  отправить письмо в личную почту пейджер

Maksim Trofimov
Если следовать алгоритму Вашей процедуры, то вот что получается:
1. Создаётся List.
2. Сканируются все элементы формы и если элемент типа TEdit, то он добавляется в List.
3. Добавленный элемент очищается.
Таким образом у Вас выполняются эти действия каждый раз, как только Вы запускаете процедуру.

© Цитата:
Видимо, я еще не готов. Вот когда стану суровым программистом... ))
А пока...лень матушка )

Суровый программист становится на основании опыта, а опыт - результат проведения тех или иных действий. А если лень - действий нет. Что тогда будет? smile
==========================================
Ваш пример нехорош для показа работы динамического массива или класса. Поэтому чтобы показать, где лучше применять динамический массив, а где класс, нужно работать с теми элементами, которые ещё не созданы, а не с теми, которые уже есть. В Вашем случае список элементов уже готов - это Form.Component. Создавать его дубль даже только с TEdit'ами бессмысленно. Попробуйте составить задачу, где список нужен не для готовых элементов, а которые Вы будете создавать при занесении в список и с которыми потом будете проводить групповую, однотипную работу. Вот к примеру:
Var
  i: Integer;
  List: TComponentList;
Begin
  List:=TComponentList.Create;
  For i:=0 To 9 Do
  Begin
     List.Add(TEdit.Create(Form));
     List[i].Parrent:=Form;
     List[i].Top:=(i+1)*20;
     List[i].Bottom:=20;
     List[i].Heght:=20;
     List[i].Width:=100;
     List[i].Text:=IntToStr(i);
  End;
End;

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Виктор Пырлик

# 12

= общий = | 24.09.2008, 14:30

© Цитата:
Это то я понял... Я вот узнать хотел что за звери такие Initialize(); Finalize(); и есть ли функция очистки, того что setlength() выделила?

Всё это, и не только, описано в учебниках. Я не "отсылаю".. smile Просто, расписывать тут теорию нет смысла. Вы гораздо больше узнаете из учебников/книг.
Коротко - мы потому и говорим "используйте методы и свойства классов и инструменты работы с классами", что классы скрывают от вас часть служебных функций.. Они не думают что "программист тупой", просто они грамотно (часто или иногда) разработаны и обязаны иметь конструкторы и деструкторы. Которые вызывают последовательно в «правильном» порядке создание, инициализацию или освобождение занимаемых ресурсов, повторяю, мы же говорим не о простых типах. А что бы не получилось – создали массив классов (TEdit например) а потом просто «освободили память для этого массива», при этом, сами экземпляры классов остались висеть в памяти (вот и утечка), и существуют методы, например Free и другие. По умолчанию, как только теряется видимость для данного объекта (создали его в процедуре или функции и выходим из неё) - вызывается Деструктор этого объекта. Так же, вызываются деструкторы и при корректном завершении приложения, но уже для всего что занимало приложение. Даже явно вызывать, например Destroy, не корректно и часто неверно. Объектное программирование – не так просто как кажется. И не корректное использование инструментов ООП сегодня приводит к гораздо большим, трудно выявляемым ошибкам – та-же утечка памяти.
К слову, почти все объекты в Delphi порождены от одного – Tobject.. и дерево это довольно развесистое.

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 13

= общий = | 24.09.2008, 14:35

sir Henry

© Цитата:
Если следовать алгоритму Вашей процедуры, то вот что получается:
1. Создаётся List.
2. Сканируются все элементы формы и если элемент типа TEdit, то он добавляется в List.
3. Добавленный элемент очищается
.

4. Выход из процедуры
5. Что происходит с List? Он удаляется из памяти?
6. Запуск процедуры.
7. 1-7

Вот что я имел ввиду )

© Цитата:
Суровый программист становится на основании опыта, а опыт - результат проведения тех или иных действий.

А delphi как раз для ленивых программистов. RAD - за программиста все делает среда ))

delphi-программисты настолько суровы, что создают ftp-сервер одной кнопкой... )


=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 14

= общий = | 24.09.2008, 14:50

Виктор Пырлик

Спасибо...
Так не охота книжки читать... Кстати говорят, что последовательное обучение даже хуже чем обучение, когда учишся на ошибках и ищешь решение проблемы в момент ее появления..

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Вадим Исаев ака sir Henry
Мастер-Эксперт

ID: 425

# 15

= общий = | 24.09.2008, 18:30 | цитировать цитировать  | профиль профиль  |  отправить письмо в личную почту пейджер

Maksim Trofimov
5. Теоретически да, память занятая List освобождается. Неосвобождается только память содержимого List. Например, Вы заходите в магазин и Вам дают коробку с конфетами. При выходе коробку у Вас отбирают. Вопрос - а конфеты? smile А вот конфеты остаются у Вас. Если их есть куда положить (карманы, например) - хорошо, конфеты Вы не потеряли. А вот если некуда, то вместе с коробкой Вы теряете и конфеты. smile

© Цитата:
Кстати говорят, что последовательное обучение даже хуже чем обучение, когда учишся на ошибках и ищешь решение проблемы в момент ее появления..

Обучение только тогда идёт хорошо, когда оно основано на осмысленных примерах. На искусственных примерах, которые совершено не трогают душу обучаемого, учиться не только бесполезно, но и вредно. Такое учение приводит только к неврозу. А уж последовательное оно или параллельное - дело десятое.
© Цитата:
delphi-программисты настолько суровы, что создают ftp-сервер одной кнопкой

Вы уверены? Это будет очень суровый сервер, который не отдаёт файлы. smile

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 16

= общий = | 25.09.2008, 01:28

sir Henry

5. Интересная теория )) Вот моя теория.
Хорошо, что список list хранит указатели на компоненты, а не их самих, что ведет к рациональному использованию памяти...
И вот на примере: Вы заходите в магазин и Вам дают коробку с конфетами. И вот привкушая как Вы будете есть очень вкусные конфетки, Вы открываете коробку, а там место каждой конфетки записка, что мол идите на склад и берите на 408 полке, 109 ящичке. И так вместо каждой конфетки, причем ящички отнють не смежные ))
Наверно после этого Вы и записочки брать то не будете, если у Вас коробку отберут...да ну ее на, скажете Вы ))

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Вадим Исаев ака sir Henry
Мастер-Эксперт

ID: 425

# 17

= общий = | 25.09.2008, 05:43 | цитировать цитировать  | профиль профиль  |  отправить письмо в личную почту пейджер

Maksim Trofimov
Я просто схожу на склад и съем рекомендованные к поеданию конфеты. smile
Да, согласен, работа с указателями это дольше и длиннее. Но в случае использования классов этот недостаток скрыт от программиста самим классом. Если вернуться к конфетам, то Вы суёте бумажку с надписью в щель считывателя и транспортёр (или отдел доставки) привозит Вам нужные конфеты.

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Maksim Trofimov

# 18

= общий = | 25.09.2008, 06:06

sir Henry

Ну что могу сказать? Вопрос исчерпан, однако вот еще что:
delphi-программисты настолько суровы, что к ним ходят конфеты сами ))

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

Вадим Исаев ака sir Henry
Мастер-Эксперт

ID: 425

# 19

= общий = | 25.09.2008, 06:44 | цитировать цитировать  | профиль профиль  |  отправить письмо в личную почту пейджер

Нет, дельфи-программисты настолько суровы, что от одного их взгляда служба доставки привозит им конфеты побросав в ужасе все остальные дела (чаепитие, перекуры и т.п.) smile

=====
Я только в одном глубоко убеждён - не надо иметь убеждений! :)

 

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

Rambler's Top100

главная страница | поддержка | задать вопрос

Время генерирования страницы: 0.21826 сек.

© 2001-2020, Портал RFPRO.RU, Россия
Калашников О.А.  |  Гладенюк А.Г.
Версия системы: 7.89 от 25.04.2020
Версия JS: 1.45 | Версия CSS: 3.39