Типы данных
Модератор: Модераторы
- Лекс Айрин
- долгожитель
- Сообщения: 5723
- Зарегистрирован: 19.02.2013 16:54:51
- Откуда: Волгоград
- Контактная информация:
olegy123, нет, конечно, но к этому стремятся.
Гуглишь TInterfacedObject . Вникаешь.
Делаешь свои классы его потомками, или сам делаешь их = class(TObject, IUnknown)
После этого все классы у тебя становятся менеджед, со счётчиком ссылок, и компилятор сам заботится чтобы экземпляр класса удалился когда на него протухнет последняя ссылка.
Только не забывать везде .Free поменять на := nil; (генерирует неявный вызов _Release)
В паскале встроенное закулисное окучивание переменных подобных типов, при каждом присваивании вставляется проверка счётчика ссылок.
Точно так же, как это делается с массивамми и строками.
Я даже больше скажу: если у вас массив, содержащий такие объекты, то при удалении массива будет выполнен проход вдоль него и удаление объектов.
Делаешь свои классы его потомками, или сам делаешь их = class(TObject, IUnknown)
После этого все классы у тебя становятся менеджед, со счётчиком ссылок, и компилятор сам заботится чтобы экземпляр класса удалился когда на него протухнет последняя ссылка.
Только не забывать везде .Free поменять на := nil; (генерирует неявный вызов _Release)
В паскале встроенное закулисное окучивание переменных подобных типов, при каждом присваивании вставляется проверка счётчика ссылок.
Точно так же, как это делается с массивамми и строками.
Я даже больше скажу: если у вас массив, содержащий такие объекты, то при удалении массива будет выполнен проход вдоль него и удаление объектов.
- serbod
- постоялец
- Сообщения: 449
- Зарегистрирован: 16.09.2016 10:03:02
- Откуда: Минск
- Контактная информация:
Cheb писал(а):После этого все классы у тебя становятся менеджед, со счётчиком ссылок, и компилятор сам заботится чтобы экземпляр класса удалился когда на него протухнет последняя ссылка.
Увы, тип class - это тупой указатель, без влияния на счетчик ссылок. Для переменных типа class счетчик ссылок не работает. Нужен тип переменной interface (которой присвоен экземпляр class), а это несколько ограничивает удобство пользования.
Но можно не заморачиваться особо с интерфейсами, а использовать "слабую ссылку" - интерфейс, содержащий только указатель на основной объект. "Слабая ссылка" будет висеть в памяти, пока кто-нибудь на нее ссылается. При освобождении основного объекта в "слабой ссылке" указатель меняется на nil, и все, кто имеет "слабую ссылку" могут этот nil увидеть, то есть не нужен сложный механизм извещений.
Код: Выделить всё
{
Sergey Bodrov (serbod@gmail.com) 2016
}
unit WeakRefs;
interface
type
IWeakRef = interface
['{4C4419E1-7ADB-47A4-826A-61E03FBB4C84}']
procedure _Clean;
function IsAlive: Boolean;
function GetOwner(): TObject;
end;
{ Слабая ссылка, которая хранит ссылку на какой-то объект и висит в памяти,
пока ее кто-то использует. Позволяет проверять, жив ли объект или нет. }
TWeakRef = class(TInterfacedObject, IWeakRef)
private
FOwner: TObject;
public
constructor Create(AOwner: TObject); virtual;
procedure _Clean;
function IsAlive: Boolean;
function GetOwner(): TObject;
end;
TWeakObject = class(TObject)
protected
FWeakRef: IWeakRef;
function GetWeakRef(): IWeakRef; virtual;
public
procedure BeforeDestruction(); override;
property WeakRef: IWeakRef read GetWeakRef;
end;
implementation
{ TWeakRef }
constructor TWeakRef.Create(AOwner: TObject);
begin
FOwner := AOwner;
end;
procedure TWeakRef._Clean();
begin
FOwner := nil;
end;
function TWeakRef.GetOwner(): TObject;
begin
Result := FOwner;
end;
function TWeakRef.IsAlive(): Boolean;
begin
Result := Assigned(FOwner);
end;
{ TWeakObject }
procedure TWeakObject.BeforeDestruction;
begin
if Assigned(FWeakRef) then
FWeakRef._Clean();
inherited;
end;
function TWeakObject.GetWeakRef: IWeakRef;
begin
if FWeakRef = nil then
begin
FWeakRef := TWeakRef.Create(Self);
end;
Result := FWeakRef;
end;
end.В коде выше TWeakObject это пример класса, использующего слабую ссылку. Можно в любой объект добавить пару методов и все.
А пример использования слабой ссылки:
Код: Выделить всё
type
{ некий ключ, у которого может быть владелец, который может вдруг исчезнуть }
TKey = class(TObject)
private
FPersonWeakRef: IWeakRef;
function GetPerson: TPerson;
procedure SetPerson(const Value: TPerson);
public
{ владелец ключа }
property Person: TPerson read GetPerson write SetPerson;
end;
function TKey.GetPerson: TPerson;
begin
if Assigned(FPersonWeakRef) and FPersonWeakRef.IsAlive then
Result := TPerson(FPersonWeakRef.GetOwner())
else
Result := nil;
end;
procedure TKey.SetPerson(const Value: TPerson);
begin
if Assigned(Value) then
FPersonWeakRef := Value.WeakRef
else
FPersonWeakRef := nil;
end;
Почитать на эту тему:
https://habrahabr.ru/post/219685/
https://habrahabr.ru/post/282035/
- Лекс Айрин
- долгожитель
- Сообщения: 5723
- Зарегистрирован: 19.02.2013 16:54:51
- Откуда: Волгоград
- Контактная информация:
serbod, а цена вопроса? А то ведь можно так дойти, что увидишь как прорисовывается мир. И тогда проще будет использовать не то что тупые указатели и проверку ссылок на актуальность в объеме всей программы. Причем, для каждой платформы своими методами.
- serbod
- постоялец
- Сообщения: 449
- Зарегистрирован: 16.09.2016 10:03:02
- Откуда: Минск
- Контактная информация:
Лекс Айрин писал(а):а цена вопроса?
Как у "голого" TObject. Слабая ссылка создается один раз при первом обращении и висит себе в памяти, пока о ней помнят.
Согласен, что это варварство, рожать аж целый TObject ради одного указателя. Но увы, нормального механизма "слабых" указателей на класс не завезли.
- Лекс Айрин
- долгожитель
- Сообщения: 5723
- Зарегистрирован: 19.02.2013 16:54:51
- Откуда: Волгоград
- Контактная информация:
serbod писал(а):Но увы, нормального механизма "слабых" указателей на класс не завезли.
обычная запись с ссылкой и счетчиком. С учетом существования у записей в современных версиях конструктора/деструктора/методов...
Ну или в самом объекте предусмотреть счетчик и функции типа Put/Push.
Код: Выделить всё
ЧетоТам(@ЧетоТам1,@ЧетоТам2);передача указателей на данные - самое стремное решение, использовать такое нужно если по другому ну совсем никак.
при использовании @ компилятор умывает руки из контроля типов параметров, всё это ложится на плечи "програмиста" и приводит к неизбежным глюкам
zub писал(а):ЧетоТам(@ЧетоТам1,@ЧетоТам2);
передача указателей на данные - самое стремное решение, использовать такое нужно если по другому ну совсем никак.
при использовании @ компилятор умывает руки из контроля типов параметров, всё это ложится на плечи "програмиста" и приводит к неизбежным глюкам
А как тогда более правильно? (если можно в примерах)
>>А как тогда более правильно? (если можно в примерах)
модификаторы var, const, constref
модификаторы var, const, constref
zub писал(а):модификаторы
А как тогда быть, если такого: ЧетоТам(@ЧетоТам1,@ЧетоТам2); - обращения требуют системные библиотеки?
смириться
- Снег Север
- долгожитель
- Сообщения: 3067
- Зарегистрирован: 27.11.2007 15:14:47
- Контактная информация:
Да, интерфейсы к сишным библиотекам такое требуют постоянно. Приходится выкручиваться. Но в любых собственно паскалевских программах за использовать подобного надо отрубать руки по самую задницу.vitaly_l писал(а):А как тогда быть, если такого: ЧетоТам(@ЧетоТам1,@ЧетоТам2); - обращения требуют системные библиотеки?
- Лекс Айрин
- долгожитель
- Сообщения: 5723
- Зарегистрирован: 19.02.2013 16:54:51
- Откуда: Волгоград
- Контактная информация:
ссылки вообще стремное дело... но приходится использовать. А уж конструкции типа ЧетоТам(@ЧетоТам1,@ЧетоТам2) это классика программирования. Другое дело, что их надо как можно быстрее инкапсулировать и стараться туда больше не лезть без надобности.
В случае серьезной работы, использование объектов очень сильно замедляет работу программ... иногда на порядки. Пример -- компонент TMemo, который, при всей его примитивности очень уж тормозит при более-менее больших текстах (((
В случае серьезной работы, использование объектов очень сильно замедляет работу программ... иногда на порядки. Пример -- компонент TMemo, который, при всей его примитивности очень уж тормозит при более-менее больших текстах (((
- serbod
- постоялец
- Сообщения: 449
- Зарегистрирован: 16.09.2016 10:03:02
- Откуда: Минск
- Контактная информация:
Снег Север писал(а):Да, интерфейсы к сишным библиотекам такое требуют постоянно. Приходится выкручиваться. Но в любых собственно паскалевских программах за использовать подобного надо отрубать руки по самую задницу.vitaly_l писал(а):А как тогда быть, если такого: ЧетоТам(@ЧетоТам1,@ЧетоТам2); - обращения требуют системные библиотеки?
const и var вполне годятся для сишных библиотек.
Добавлено спустя 5 минут 1 секунду:
Лекс Айрин писал(а):В случае серьезной работы, использование объектов очень сильно замедляет работу программ... иногда на порядки. Пример -- компонент TMemo, который, при всей его примитивности очень уж тормозит при более-менее больших текстах (((
Объекты языка не тормозят, там самые тормоза только при выделении-освобождении мапяти. А вот объекты GUI это целое приложение, управляемое сообщениями. Но там есть свои фокусы, например, для списков и таблиц можно использовать "owner data", и тогда отображение миллионов элементов будет тормозить не более, чем количество видимых на экране элементов.
Увы, тип class - это тупой указатель, без влияния на счетчик ссылок. Для переменных типа class счетчик ссылок не работает. Нужен тип переменной interface (которой присвоен экземпляр class), а это несколько ограничивает удобство пользования.
Чёзабред, наследуешь от IUnknown, делая всё ручками, или от TInterfacedObject, где всё уже сделано за нас - и класс становится со счётчиком ссылок, автоудаляющийся при присваивании nil последней ссылке на него.
Объяснил же уже, этот механизм чуть ли не с Дельфи 2 существует.
Цена вопроса - два лишних поля в экземпляре класса: ссылка на интерфейс и счётчик ссылок.
Пример -- компонент TMemo, который, при всей его примитивности очень уж тормозит
Вот потому и тормозит, что примитивный, и кпд у него ниже плинтуса.
А не потому, что он - класс.
Но в любых собственно паскалевских программах за использовать подобного надо отрубать руки по самую задницу.
Согласен с одной оговоркой: если пишешь API интерфейса между исполняемыми модулями с независимыми диспетчерами памяти - без указателей не обойтись. Потому что ни массив, ни строку не передашь: выйдет хряп-с.
const и var вполне годятся для сишных библиотек.
А вот за такое лапки оттяпывать надо уже пейсателю такой конверсии хидера.
!Внезапно! требуется передать NULL - и слов у тебя не остаётся, кроме матерных.
