Типы данных

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Ответить
Аватара пользователя
Лекс Айрин
долгожитель
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград
Контактная информация:

Сообщение Лекс Айрин »

olegy123, нет, конечно, но к этому стремятся.
Аватара пользователя
Cheb
энтузиаст
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34
Контактная информация:

Сообщение Cheb »

Гуглишь TInterfacedObject . Вникаешь.
Делаешь свои классы его потомками, или сам делаешь их = class(TObject, IUnknown)

После этого все классы у тебя становятся менеджед, со счётчиком ссылок, и компилятор сам заботится чтобы экземпляр класса удалился когда на него протухнет последняя ссылка.
Только не забывать везде .Free поменять на := nil; (генерирует неявный вызов _Release)

В паскале встроенное закулисное окучивание переменных подобных типов, при каждом присваивании вставляется проверка счётчика ссылок.
Точно так же, как это делается с массивамми и строками.
Я даже больше скажу: если у вас массив, содержащий такие объекты, то при удалении массива будет выполнен проход вдоль него и удаление объектов.
Аватара пользователя
serbod
постоялец
Сообщения: 449
Зарегистрирован: 16.09.2016 10:03:02
Откуда: Минск
Контактная информация:

Сообщение serbod »

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
Откуда: Минск
Контактная информация:

Сообщение serbod »

Лекс Айрин писал(а):а цена вопроса?

Как у "голого" TObject. Слабая ссылка создается один раз при первом обращении и висит себе в памяти, пока о ней помнят.

Согласен, что это варварство, рожать аж целый TObject ради одного указателя. Но увы, нормального механизма "слабых" указателей на класс не завезли.
Аватара пользователя
Лекс Айрин
долгожитель
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград
Контактная информация:

Сообщение Лекс Айрин »

serbod писал(а):Но увы, нормального механизма "слабых" указателей на класс не завезли.


обычная запись с ссылкой и счетчиком. С учетом существования у записей в современных версиях конструктора/деструктора/методов...
Ну или в самом объекте предусмотреть счетчик и функции типа Put/Push.
zub
долгожитель
Сообщения: 2889
Зарегистрирован: 14.11.2005 22:51:26
Контактная информация:

Сообщение zub »

Код: Выделить всё

ЧетоТам(@ЧетоТам1,@ЧетоТам2);

передача указателей на данные - самое стремное решение, использовать такое нужно если по другому ну совсем никак.
при использовании @ компилятор умывает руки из контроля типов параметров, всё это ложится на плечи "програмиста" и приводит к неизбежным глюкам
Аватара пользователя
vitaly_l
долгожитель
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41
Контактная информация:

Сообщение vitaly_l »

zub писал(а):ЧетоТам(@ЧетоТам1,@ЧетоТам2);

передача указателей на данные - самое стремное решение, использовать такое нужно если по другому ну совсем никак.
при использовании @ компилятор умывает руки из контроля типов параметров, всё это ложится на плечи "програмиста" и приводит к неизбежным глюкам

А как тогда более правильно? (если можно в примерах)
zub
долгожитель
Сообщения: 2889
Зарегистрирован: 14.11.2005 22:51:26
Контактная информация:

Сообщение zub »

>>А как тогда более правильно? (если можно в примерах)
модификаторы var, const, constref
Аватара пользователя
vitaly_l
долгожитель
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41
Контактная информация:

Сообщение vitaly_l »

zub писал(а):модификаторы

А как тогда быть, если такого: ЧетоТам(@ЧетоТам1,@ЧетоТам2); - обращения требуют системные библиотеки?
zub
долгожитель
Сообщения: 2889
Зарегистрирован: 14.11.2005 22:51:26
Контактная информация:

Сообщение zub »

смириться
Аватара пользователя
Снег Север
долгожитель
Сообщения: 3067
Зарегистрирован: 27.11.2007 15:14:47
Контактная информация:

Сообщение Снег Север »

vitaly_l писал(а):А как тогда быть, если такого: ЧетоТам(@ЧетоТам1,@ЧетоТам2); - обращения требуют системные библиотеки?
Да, интерфейсы к сишным библиотекам такое требуют постоянно. Приходится выкручиваться. Но в любых собственно паскалевских программах за использовать подобного надо отрубать руки по самую задницу.
Аватара пользователя
Лекс Айрин
долгожитель
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград
Контактная информация:

Сообщение Лекс Айрин »

ссылки вообще стремное дело... но приходится использовать. А уж конструкции типа ЧетоТам(@ЧетоТам1,@ЧетоТам2) это классика программирования. Другое дело, что их надо как можно быстрее инкапсулировать и стараться туда больше не лезть без надобности.

В случае серьезной работы, использование объектов очень сильно замедляет работу программ... иногда на порядки. Пример -- компонент TMemo, который, при всей его примитивности очень уж тормозит при более-менее больших текстах (((
Аватара пользователя
serbod
постоялец
Сообщения: 449
Зарегистрирован: 16.09.2016 10:03:02
Откуда: Минск
Контактная информация:

Сообщение serbod »

Снег Север писал(а):
vitaly_l писал(а):А как тогда быть, если такого: ЧетоТам(@ЧетоТам1,@ЧетоТам2); - обращения требуют системные библиотеки?
Да, интерфейсы к сишным библиотекам такое требуют постоянно. Приходится выкручиваться. Но в любых собственно паскалевских программах за использовать подобного надо отрубать руки по самую задницу.


const и var вполне годятся для сишных библиотек.

Добавлено спустя 5 минут 1 секунду:
Лекс Айрин писал(а):В случае серьезной работы, использование объектов очень сильно замедляет работу программ... иногда на порядки. Пример -- компонент TMemo, который, при всей его примитивности очень уж тормозит при более-менее больших текстах (((


Объекты языка не тормозят, там самые тормоза только при выделении-освобождении мапяти. А вот объекты GUI это целое приложение, управляемое сообщениями. Но там есть свои фокусы, например, для списков и таблиц можно использовать "owner data", и тогда отображение миллионов элементов будет тормозить не более, чем количество видимых на экране элементов.
Аватара пользователя
Cheb
энтузиаст
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34
Контактная информация:

Сообщение Cheb »

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

Чёзабред, наследуешь от IUnknown, делая всё ручками, или от TInterfacedObject, где всё уже сделано за нас - и класс становится со счётчиком ссылок, автоудаляющийся при присваивании nil последней ссылке на него.
Объяснил же уже, этот механизм чуть ли не с Дельфи 2 существует.
Цена вопроса - два лишних поля в экземпляре класса: ссылка на интерфейс и счётчик ссылок.

Пример -- компонент TMemo, который, при всей его примитивности очень уж тормозит

Вот потому и тормозит, что примитивный, и кпд у него ниже плинтуса.
А не потому, что он - класс.

Но в любых собственно паскалевских программах за использовать подобного надо отрубать руки по самую задницу.

Согласен с одной оговоркой: если пишешь API интерфейса между исполняемыми модулями с независимыми диспетчерами памяти - без указателей не обойтись. Потому что ни массив, ни строку не передашь: выйдет хряп-с.

const и var вполне годятся для сишных библиотек.

А вот за такое лапки оттяпывать надо уже пейсателю такой конверсии хидера.
!Внезапно! требуется передать NULL - и слов у тебя не остаётся, кроме матерных.
Ответить