Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Строку в объект [D7, XP]


Страницы:   1 2 3 4 5 6

kyn66 ©   (2009-11-12 16:11:00) 

Подскажите, каким способом можно строку вставить в объект , ну например ComboBox ? Нужно к примеру просканировать текущий каталог диска, найденные файлы записать в Combobox, а полный путь к файлу сохранить в объекте. Помню со строками есть проблемы при сохранении в объект. Есть вот такой вариант

....
var
 PS: PString;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
 new(PS);
 PS^ := 'Hello world 1';
 ComboBox1.Items.AddObject('Item1', TObject(PS));
 PS^ := 'Hello world 2';
 ComboBox1.Items.AddObject('Item2', TObject(PS));
 PS^ := 'Hello world 3';
 ComboBox1.Items.AddObject('Item3', TObject(PS));
...

procedure TForm1.FormDestroy(Sender: TObject);
begin
 Dispose(PS);
end;


При доступе к объекту

procedure TForm1.ComboBox1Change(Sender: TObject);
begin
 ShowMessage(PString(Combobox1.Items.Objects[0])^); //Hello world 1
end;


возникает ошибка (AV)


Leonid Troyanovsky   (2009-11-16 10:11:00) [21]


> kyn66 ©   (16.11.09 10:03) [85]

> блок памяти. И имя переменной, связанной с конкретным блоком
> мы как таковое теряем. Т.е. к конкретному блоку по имени
> мы обратиться не сможем. Поэтому мы в цикле пробегаем по
> всем итемам и удаляем, если что то там есть

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

--
Regards, LVT.


kyn66   (2009-11-16 10:03:00) [22]


> Заполнять не обязательно. Обязательно другое - помнить адрес
> выделенного блока, чтобы потом можно было его освободить.
>


Но в данном случае (заполнение в цикле) переменная инициализируется, получает/не получает значение, инициализируется на новый блок памяти. И имя переменной, связанной с конкретным блоком мы как таковое теряем. Т.е. к конкретному блоку по имени мы обратиться не сможем. Поэтому мы в цикле пробегаем по всем итемам и удаляем, если что то там есть

for i:=0 to ListView1.Items.Count-1 do
 Dispose(PMyRecord(ListView1.Items[i].Data));
end;


> Есть АДРЕС выделенного блока, а есть СОДЕРЖИМОЕ этого блока.
>  Это совершенно разные вещи. Адрес хранится в переменной
> типа "указатель", а содержимое хранится в самом блоке.

Не, с этим похоже разобрался уже.


Leonid Troyanovsky   (2009-11-16 09:54:00) [23]


> kyn66 ©   (16.11.09 09:24) [78]

>   if PMyRecord(ListView1.Items[i].Data)) <> nil

Здесь приведение лишнее, бо загромождает суть.

--
Regards, LVT.


Leonid Troyanovsky   (2009-11-16 09:47:00) [24]


> sniknik ©   (16.11.09 09:30) [80]

В начале было New.

--
Regards, LVT.


sniknik   (2009-11-16 09:33:00) [25]

а ну да, если у тебя там "дыры" то значит повторное использование тоже будет. значит
FreeAndNil(PMyRecord(ListView1.Items[i].Data));


Юрий Зотов   (2009-11-16 09:30:00) [26]

> kyn66 ©   (16.11.09 09:24) [78]

> Получается так, что если память выделена, то ее обязательно нужно
> чем-то заполнить.

Заполнять не обязательно. Обязательно другое - помнить адрес выделенного блока, чтобы потом можно было его освободить.

Похоже, Вы пока еще немного путаетесь. Есть АДРЕС выделенного блока, а есть СОДЕРЖИМОЕ этого блока. Это совершенно разные вещи. Адрес хранится в переменной типа "указатель", а содержимое хранится в самом блоке.


sniknik   (2009-11-16 09:30:00) [27]

> Dispose(PMyRecord(ListView1.Items[i].Data));
PMyRecord(ListView1.Items[i].Data).Free;


RWolf   (2009-11-16 09:29:00) [28]


> Получается так, что если память выделена, то ее обязательно
> нужно чем-то заполнить

К выделенной памяти только одно требование — её обязательно
нужно когда-нибудь освободить. А уж заполнять или нет — это на усмотрение программиста.


kyn66   (2009-11-16 09:24:00) [29]


> Главное не это, а то, что перед тем, как переменная получила
> новое значение, ее старое значение было сохранено в Items[i].
> Datа. Поэтому адреса выделенных блоков не теряются и мы
> имеем возможность вызвать Dispose для всех блоков.


Получается так, что если память выделена, то ее обязательно нужно чем-то заполнить. Если же проходя в цикле не выполнить ListView1.AddItem('Entry' + IntToStr(i), Pointer(pRec));то и в этом случае получится незаполненная дыра. И не выскочит ли тогда ошибка при удалении объектов

for i:=0 to ListView1.Items.Count-1 do
  Dispose(PMyRecord(ListView1.Items[i].Data));
end;

если предварительно не проверить

  if PMyRecord(ListView1.Items[i].Data)) <> nil
   ...


Leonid Troyanovsky   (2009-11-16 09:13:00) [30]


> Anatoly Podgoretsky ©   (16.11.09 09:02) [76]

> Одного желания мало.

Да, конечно.
Если нет способности к восприятию информации,
в т.ч. из-за слабой подготовки, то вместо диалога
получаются несвязные монологи.

--
Regards, LVT.


Anatoly Podgoretsky   (2009-11-16 09:02:00) [31]

> Leonid Troyanovsky  (16.11.2009 07:59:15)  [75]

Одного желания мало.


Leonid Troyanovsky   (2009-11-16 07:59:00) [32]


> kyn66 ©   (15.11.09 02:33) [62]

>  Хотелость попробывать изучить работу объектов.

Был хороший повод изучить "виртуальные" контролы.

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

Обсуждения позволяют избавиться от заблуждений.
Желающим, конечно.

--
Regards, LVT.


Leonid Troyanovsky   (2009-11-16 07:43:00) [33]


> Дмитрий Белькевич   (15.11.09 21:16) [66]

> Если нужен и Integer (для последующего его применения) и
> строчка (для отображения) то строчка запихивается в комбобокс,
>  integer - в указатель:

> AddObject(s, Pointer(i))

Integer не годится, бо stdctrls.pas

function TCustomComboBoxStrings.GetObject(Index: Integer): TObject;
begin
 Result := TObject(SendMessage(ComboBox.Handle, CB_GETITEMDATA, Index, 0));
 if Longint(Result) = CB_ERR then
   Error(SListIndexError, Index);
end;

-1 для классов LISTBOX & COMBOBOX - индикатор ошибки.

--
Regards, LVT.


Германн   (2009-11-16 02:25:00) [34]


> kyn66 ©

Везёт тебе! Постарайся не упустить это везение!


Юрий Зотов   (2009-11-16 00:16:00) [35]

> kyn66 ©   (15.11.09 22:56) [71]

> А в моем случае переменная одна и та же, но после инициализации она
> сделала "доброе дело", присвоив этому адресу некие данные, а уж потом
> получила новый адрес.

Главное не это, а то, что перед тем, как переменная получила новое значение, ее старое значение было сохранено в Items[i].Datа. Поэтому адреса выделенных блоков не теряются и мы имеем возможность вызвать Dispose для всех блоков.

> Я все правильно понял?

Да, код верный.


kyn66   (2009-11-15 22:56:00) [36]


> но после второго New адрес первого выделенного блока будет
> безвозвратно потерян

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

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
 i:integer;
begin
 for i:=0 to ListView1.Items.Count-1 do
   Dispose(PMyRecord(ListView1.Items[i].Data));
end;

наилучшим способом очистит память... Я все правильно понял?

> Delphi автоматически разыменовывает указатели, поэтому значок
> ^ при разыменовании можно не писать.

Вот так вот....

> поэтому многие (и я в том числе) используют такое правило
> - разыменовывать указатели на объекты

Что считается в программировании  - правилом хорошего тона...


Юрий Зотов   (2009-11-15 22:23:00) [37]

> kyn66 ©   (15.11.09 22:15) [68]

> pRec^.id - конкретно переменная,
> а pRec.id указатель на адрес в памяти

Обе формы записи равнозначны. Delphi автоматически разыменовывает указатели, поэтому значок ^ при разыменовании можно не писать. Но это не всегда хорошо (снижается читабельность кода), поэтому многие (и я в том числе) используют такое правило - разыменовывать указатели на объекты (то есть, переменные любого объектного типа) поручают компилятору, а остальные указатели разыменовывают явно, указывая значок ^.


Юрий Зотов   (2009-11-15 22:17:00) [38]

> kyn66 ©   (15.11.09 21:59) [67]

За освобождение объектов Delphi не отвечает, это должен делать программист. Правда, есть еще такая штука, как интерфейсы, там все хитрее - видимо, Вам теперь стоит разобраться и с ними, чтобы эту тему закрыть.

Что касается примеров кода, то между ними есть большая разница.

var
 IntPointer: ^Integer;
begin
  New(IntPointer);
  New(IntPointer);
end.

Здесь память выделяется дважды, но после второго New адрес первого выделенного блока будет безвозвратно потерян (затерт вторым адресом, потому что второй адрес пишется в ту же переменную, которая до этого хранила первый адрес). Теперь первому блоку нельзя сделать Dispose (нечего передать в параметре) - чистейшая утечка памяти.

procedure TForm1.Button3Click(Sender: TObject);
var
 i: Integer;
 pRec: PMyRecord;
begin
 for i := 0 to 10 do
 begin
    new(pRec);
    pRec^.id := i;
    pRec^.Name := 'Entry' + IntToStr(i);
    ListView1.AddItem('Entry' + IntToStr(i), Pointer(pRec));
 end;
end;

Здесь для выделения 11 блоков памяти тоже использована одна переменная, которая, конечно, каждый раз затирается новым адресом. Но старый адрес при этом не теряется, потому что его копия запоминается в Items[i].Data и мы можем вызвать Dispose, используя эту копию.


kyn66   (2009-11-15 22:15:00) [39]


> Жаль только применение крайне неудачное.

Это частный случай, просто нужно было что-то привести в качестве примера, чтобы поняли что нужно.


> Понять указатели? Ну можешь мои статьи посмотреть:

Спасибо, посмотрю. Кстати, по поводу разыминования.... Только чур ногами не бить, чисто эксперимент. Если из вышеприведенного кода [67] в момент присваивания значений

new(pRec);
    pRec^.id := i;
    pRec^.Name := 'Entry' + IntToStr(i);


Сперва я присвоил как в коде, потом убрал знак ^и опять выполнил присваивание. Хотя понимаю, что pRec^.id- конкретно переменная, а pRec.idуказатель на адрес в памяти, где эта меременная находится. После проверки обеих вариантов через

procedure TForm1.ListView1Click(Sender: TObject);
var
 i: Integer;
 xRec: PMyRecord;
begin
 for i := 0 to Listview1.Items.Count - 1 do
  if ListView1.Items[i].Selected then
   begin
     xRec := PMyRecord(ListView1.Items[i].Data);
     ShowMessage(Format('Record #%d Name: %s', [xRec.id, xRec.Name]));
   end;
end;


Значения были одинаковые. Совпадение?

PS
Касаемо литературы и прочих источников информации. Delphi World у меня как настольная книга, которая всегда под руками. Хочу сказать, что там не без ошибок коды приведены. А в книгах? Не может быть очепятки? Вот форум и помогает соединить все вопросы и дать правильный ответ.


kyn66   (2009-11-15 21:59:00) [40]


> Юрий Зотов ©   (15.11.09 10:34) [65]
> .. а что там понимать? Указатель - это переменная, содержащая
> адрес. Только и всего.

Это сейчас я уже понял. Просто раньше небыло нужды в их изучении и ессно читать про это не хотелось. Сейчас заинтересовало.

>Кстати, длинные строки, динамические
> массивы, объекты - все это тоже указатели.

Да, но за их уничтожение отвечает Delphi.

Я вот тут статейку почтал интересную http://www.citforum.ru/programming/bp70_ug/bp70ug_08.shtml. Так вот там есть подраздел "Потери динамически распределяемой памяти"Так вот там не совсем понятен один момент

Общей причиной утечек памяти является переприсваивание динамических переменных без освобождения  предыдущих. Простейшим случаем является следующий:
   var IntPointer: ^Integer;
      begin
         New(IntPointer);
         New(IntPointer);
      end.


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

type
  PMyRecord = ^TMyRecord;
  TMyRecord = record
    id: Integer;
    Name: string;
  end;
....
procedure TForm1.Button3Click(Sender: TObject);
var
 i: Integer;
 pRec: PMyRecord;
begin
 for i := 0 to 10 do
  begin
    new(pRec);
    pRec^.id := i;
    pRec^.Name := 'Entry' + IntToStr(i);
    ListView1.AddItem('Entry' + IntToStr(i), Pointer(pRec));
  end;
end;


Не будет ли это нарушением? Точнее не спровацирetn kb это утечку? Хотя опять же проверял перед инициализацией и после удаления через  ShowMessage(IntToStr(AllocMemSize)); Все вроде нормально...


Страницы:   1 2 3 4 5 6

Наверх

Титульная страница Поиск, карта сайта Написать письмо