Класс-обёртка для integer (например)...

Вопросы программирования и использования среды Lazarus.

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

Аватара пользователя
leo_bsv
постоялец
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола
Контактная информация:

Класс-обёртка для integer (например)...

Сообщение leo_bsv »

К примеру определён такой класс

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

type
  TPositiveInteger = class
  private
    FValue: integer;
    procedure SetValue(AValue:integer);
  public
    property Value: integer read FValue write SetValue;
  end;                               

можно ли свойство Value сделать таким "умолчательным" что значение данного свойства выводилось бы сразу при обращении к объекту этого класса, т.е. чтобы не лезть в свойство за значением...
т.е. с описанным классом получится так

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

var x:TPositiveInteger;
...
x.Value:=...

а хочется вот так :!:

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

var x:TPositiveInteger;
...
x:=...

знаю - я сосем обнаглел :wink: но вдруг есть в жизни счастье :?:
Аватара пользователя
Brainenjii
энтузиаст
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Сообщение Brainenjii »

Можно нескромный вопрос - а зачем это? Мне часто нужно было Integer как объект для работы со списком чисел. Потом появились Generic'и, а потом я узнал, что можно работать и с обычным TList'ом:

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

..
  TIntegerList = Specialize TFPGList<Integer>;
..
Var
  aList: TList;
  aIntegerList: TIntegerList;
Begin
  aList := TList.Create;
  aIntegerList := TIntegerList.Create;
  For i := 0 To 10 Do
    Begin
      aList.Add(TObject(i * 2));
      aIntegerList.Add(i * 2);
    End;
  For i := 0 To 10 Do
    WriteLn(Integer(aList[i]), aIntegerList[i]);
End;
..
Аватара пользователя
AlexVinS
новенький
Сообщения: 95
Зарегистрирован: 27.01.2009 00:18:01

Сообщение AlexVinS »

Можно переопределить оператор присваивания.

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

operator := (r: Integer) res: TPositiveInteger;
begin
 res := TPositiveInteger.Create;
 res.Value := r;
end;

operator := (r: TPositiveInteger) res: Integer;
begin
 res := r.Value ;

end;



Главное на мой взгляд избегать утечек памяти при таком подходе. А вобщем должно работать.
Аватара пользователя
leo_bsv
постоялец
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола
Контактная информация:

Сообщение leo_bsv »

Brainenjii писал(а):Можно нескромный вопрос - а зачем это?

это затем что хочется контролировать вводимое значение, т.е. TPositiveInteger - это значение > 0,
а например сделать класс TNonNegativeInteger - значение 0 и более...
соответственно TNegativeInteger - значение < 0...
далее есть объявление классов...

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

type
  TPositiveInteger = class
  private
    FValue: integer;
    procedure SetValue(AValue:integer);
  public
    property Value: integer read FValue write SetValue;
  end;                 
...
  TNonNegativeInteger = class             
...
  TNegativeInteger = class             

в методах которых описана функция SetValue, которая и контролирует вводимые значения,
затем описываем любой класс

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

type
  TVasiaPupkinClass = class
  private
    FPositiveField: TPositiveInteger;
    FNonNegativeField: TNonNegativeInteger;
    FNegativeField: TNegativeInteger;
  public
    PositiveField: TPositiveInteger read FPositiveField write FPositiveField;
    NonNegativeField: TNonNegativeInteger read FNonNegativeField write FNonNegativeField;
    NegativeField: TNegativeInteger read FNegativeField write FNegativeField;
  end;

Т.е. в классе TVasiaPupkinClass просто происходит присвоение значения полю, а ввод контролирует процедура SetValue класса TPositiveInteger, TNonNegativeInteger, TNegativeInteger по задумке автора это должно сократить объём кода :P
Прямое же присвоение значения нужно для удобопользования... классы TPositiveInteger, TNonNegativeInteger, TNegativeInteger простые - свойство то одно всего...

Добавлено спустя 5 часов 8 минут 52 секунды:
AlexVinS писал(а):Можно переопределить оператор присваивания.

чуть выше в этом же посте описано что таких классов несколько - можно для каждого переопределить?
в теле переопределения оператора присваивания должно содержаться условие с проверкой отсекающей ненужные значения и выдающее исключение... такой подход вообще практикуют :?:
MageSlayer
постоялец
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Сообщение MageSlayer »

... по задумке автора это должно сократить объём кода...


ООП уменьшает размер кода.
Супер. Посмеялся, спасибо.

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

И вообще, как там говорили классики - "Лучше иметь сто функций работающих с одним типом данных, чем 10 функций, работающих с 10 типами данных".
Вы же, похоже, делаете все в точности наоборот.
Аватара пользователя
alexs
долгожитель
Сообщения: 4069
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь
Контактная информация:

Сообщение alexs »

leo_bsv писал(а):это затем что хочется контролировать вводимое значение, т.е. TPositiveInteger - это значение > 0,
а например сделать класс TNonNegativeInteger - значение 0 и более...
соответственно TNegativeInteger - значение < 0...


А если просто объявить перечисляемый тип?

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

type
  TNegativeInteger = - (MaxInt - 1) .. -1;
  TNonNegativeInteger = 0 ..  MaxInt;
  TPositiveInteger = 1 ..MaxInt;
Аватара пользователя
leo_bsv
постоялец
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола
Контактная информация:

Сообщение leo_bsv »

alexs писал(а):А если просто объявить перечисляемый тип?

Да, то что надо. Спасибо за дельный совет.
С integer всё понятно, а вот с double как быть? думаю приемлемо будет переопределить оператор присваивания и контролировать диапазон в нём :?:
MageSlayer писал(а):Вы же, похоже, делаете все в точности наоборот.

Отойди, ты заслоняешь мне солнце...
Аватара пользователя
AlexVinS
новенький
Сообщения: 95
Зарегистрирован: 27.01.2009 00:18:01

Сообщение AlexVinS »

alexs писал(а):...

А если просто объявить перечисляемый тип?

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

type
  TNegativeInteger = - (MaxInt - 1) .. -1;
  TNonNegativeInteger = 0 ..  MaxInt;
  TPositiveInteger = 1 ..MaxInt;


То для проверки вводимых значений придется включать range checks (потребует определенных усилий чтобы включить только там где надо, а еще большой вопрос КАК вводятся значения. Компилятор проверит ТОЛЬКО константы) или всеравно проверять вручную. Нет, пока решения лучше класса-обертка не представлено.

Добавлено спустя 11 минут 21 секунду:
leo_bsv писал(а):чуть выше в этом же посте описано что таких классов несколько - можно для каждого переопределить?
в теле переопределения оператора присваивания должно содержаться условие с проверкой отсекающей ненужные значения и выдающее исключение... такой подход вообще практикуют :?:


1. Можно переопределять для каждого класса. (причем для констант удобно использовать подтипы - subrange types - которые выше почему-то названы перечислимыми. И для их оператор присваивания тоже можно переопределить, причем можно и в обход проверки).

2. Проверка правильности должна быть в одном месте например в SetValue или вообще отдельный метод (зависит от конкретной задачи).
Аватара пользователя
alexs
долгожитель
Сообщения: 4069
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь
Контактная информация:

Сообщение alexs »

AlexVinS писал(а):То для проверки вводимых значений придется включать range checks (потребует определенных усилий чтобы включить только там где надо, а еще большой вопрос КАК вводятся значения. Компилятор проверит ТОЛЬКО константы) или всеравно проверять вручную. Нет, пока решения лучше класса-обертка не представлено.

Допустимость данных надо проверять при их появлении.
Проверка при каждом чихе - не есть хорошо.
Пусть ваш интерфейс ввода обеспечит появление корректных данных.
Аватара пользователя
leo_bsv
постоялец
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола
Контактная информация:

Сообщение leo_bsv »

alexs писал(а):Пусть ваш интерфейс ввода обеспечит появление корректных данных.

хм... похоже вернулись к тому с чего начали - проверять ввод в функции SetValue...
вопрос вообще-то был - как упростить присвоение, опустив в операторе соответствующий метод... в принципе ответ получен - с помощью переопределения оператора.
Аватара пользователя
alexs
долгожитель
Сообщения: 4069
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь
Контактная информация:

Сообщение alexs »

leo_bsv писал(а):хм... похоже вернулись к тому с чего начали - проверять ввод в функции SetValue...

Нет
Именно для таких случае используем свой тип с ограничем на данные - это уменьшает вероятность появления ошибок внутри системы.
А вот на уровне взаимодействия системы с внешним видом ставим проверки на коректность данных. Для этих проверок - делать специальный класс - избыточно. Достаточно обычного процедурного подхода.

По моему мнению - надо соблюдать баланс. Т.е. до определённого уровня сложности обрабатываемого объекта - достаточно использовать процедурный подход.
Но всегда есть грань, начиная с которой для формализации действий уже лучше использовть объектный подход на все 200%.
Просто ваша проблема с контролем данных - это чисто процедурная задача, причём очень низкого уровня.
Аватара пользователя
leo_bsv
постоялец
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола
Контактная информация:

Сообщение leo_bsv »

alexs писал(а):Проверка при каждом чихе - не есть хорошо.
Пусть ваш интерфейс ввода обеспечит появление корректных данных.

у меня не интерфейс - это всё мне нужно для ODFProc...
есть готовый файл - хмл кстати из .odt, а эти странные типы обозначены в спецификации OASIS Open Docment Format...
поэтому влиять на чтение из файла нет смысла, можно читать и в integer... а вот проверку при записи производить нужно... т.к. все эти типы имеют логическую основу.
можно конечно для каждого поля описываемого класса прописывать Set_ процедуру и в ней осуществлять нужную проверку, но это не удобно - удобнее сразу описать поле определенным типом и в нем контролировать ввод, но тут натыкаюсь на атрибут, или переопределять оператор присваивания чтоб атрибут не вводить при дальнейшем использовании модуля, или через Set_ для каждого свойства и обойтись без этих типов... с типами конечно и код гораздо прозрачней, но переопределять оператор присваивания... хз... буду экспериментировать.

Добавлено спустя 36 минут 43 секунды:
AlexVinS писал(а):Главное на мой взгляд избегать утечек памяти при таком подходе. А вобщем должно работать.

как отследить есть утечка или нет?
если в качестве типа свойства используется собственный класс, то при уничтожении класса-владельца данного свойства объект находящийся в этом свойстве может уничтожаться автоматически или его нужно всегда освобождать в деструкторе класса? как отследить все объекты типа TPositiveInteger?
alexs писал(а):Проверка при каждом чихе - не есть хорошо.

в моём случае - это так же страшно как функция Set... прикрепляемая к любому полю создаваемого класса...

Добавлено спустя 47 минут 18 секунд:
Free Pascal : Reference guide. писал(а):The difference between objects and classes is mainly that an object is allocated on the stack, as an ordinary record would be, and that classes are always allocated on the heap. In the following example:
Var
A : TSomeObject; // an Object
B : TSomeClass; // a Class

The main difference is that the variable A will take up as much space on the stack as the size of the object (TSomeObject). The variable B, on the other hand, will always take just the size of a pointer on the stack. The actual class data is on the heap.

From this, a second difference follows: a class must always be initialized through its constructor, whereas for an object, this is not necessary. Calling the constructor allocates the necessary memory on the heap for the class instance data.

Может быть описать эти типы не как классы а как объекты :?:
Odyssey
энтузиаст
Сообщения: 580
Зарегистрирован: 29.11.2007 16:32:24

Сообщение Odyssey »

leo_bsv писал(а):можно конечно для каждого поля описываемого класса прописывать Set_ процедуру и в ней осуществлять нужную проверку, но это не удобно - удобнее сразу описать поле определенным типом и в нем контролировать ввод, но тут натыкаюсь на атрибут, или переопределять оператор присваивания чтоб атрибут не вводить при дальнейшем использовании модуля, или через Set_ для каждого свойства и обойтись без этих типов... с типами конечно и код гораздо прозрачней, но переопределять оператор присваивания...

Имхо №1: удобство внутри библиотеки -- вещь вторичная по сравнению с удобством её использования. Поэтому вариант, когда пользователь должен у каждого свойства вызывать SetValue, я бы отбросил. Лучше вынести проверку в отдельную процедуру и нагенерировать сеттеров (SetXXX) с вызовом этой процедуры. Автоматически генерировать сеттеры может и Lazarus, останется только вставить в них вызов процедуры проверки.
Имхо №2: перегрузку операторов в коде на Object Pascal без крайней на то необходимости лучше не делать.
leo_bsv писал(а):если в качестве типа свойства используется собственный класс, то при уничтожении класса-владельца данного свойства объект находящийся в этом свойстве может уничтожаться автоматически или его нужно всегда освобождать в деструкторе класса?

Нужно всегда освобождать в деструкторе.
iskander
энтузиаст
Сообщения: 630
Зарегистрирован: 08.01.2012 18:43:34

Сообщение iskander »

leo_bsv писал(а):как отследить есть утечка или нет?

модуль heaptrc?
Аватара пользователя
leo_bsv
постоялец
Сообщения: 276
Зарегистрирован: 04.08.2010 16:26:10
Откуда: Йошкар-Ола
Контактная информация:

Сообщение leo_bsv »

Odyssey писал(а):Лучше вынести проверку в отдельную процедуру и нагенерировать сеттеров (SetXXX) с вызовом этой процедуры. Автоматически генерировать сеттеры может и Lazarus, останется только вставить в них вызов процедуры проверки.

ну примерно это и делаю... код конечно получается переполнен кучей двухстрочных процедур...

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

...
    procedure SetNumberColumnsRepeated(AValue: integer);
    procedure SetNumberColumnsSpanned(AValue: integer);
    procedure SetNumberMatrixColumnsSpanned(AValue: integer);
    procedure SetNumberMatrixRowsSpanned(AValue: integer);
    procedure SetNumberRowsSpanned(AValue: integer);
...
// процедура проверки условия...
procedure SetPosiviteInteger(var F: integer; const AValue:integer);
begin
  if AValue<=0 then
    raise EInOutError.Create('Value mast be > 0.')
  else F:=AValue;
end;

{Процедуры SetXXXX}

procedure TOdtTableCell.SetNumberColumnsRepeated(AValue: integer);
begin
  if FNumberColumnsRepeated=AValue then Exit;
  SetPosiviteInteger(FNumberColumnsRepeated,AValue);
end;

procedure TOdtTableCell.SetNumberColumnsSpanned(AValue: integer);
begin
  if FNumberColumnsSpanned=AValue then Exit;
  SetPosiviteInteger(FNumberColumnsSpanned,AValue);
end;

procedure TOdtTableCell.SetNumberMatrixColumnsSpanned(AValue: integer
  );
begin
  if FNumberMatrixColumnsSpanned=AValue then Exit;
  SetPosiviteInteger(FNumberMatrixColumnsSpanned,AValue);
end;

procedure TOdtTableCell.SetNumberMatrixRowsSpanned(AValue: integer);
begin
  if FNumberMatrixRowsSpanned=AValue then Exit;
  SetPosiviteInteger(FNumberMatrixRowsSpanned,AValue);
end;

procedure TOdtTableCell.SetNumberRowsSpanned(AValue: integer);
begin
  if FNumberRowsSpanned=AValue then Exit;
  SetPosiviteInteger(FNumberRowsSpanned,AValue);
end;

Минус такого подхода - не видно какой тип у каждого поля... т.е. для пишущего и в дальнейшем использующего модуль человека не ясен будет принцип разбора вводимого значения (нужно будет рыться в коде чтобы понять)... а если объявить поля соответствующим типом, например TPositiveInteger, то из названия понятно с чем имеешь дело.
Ответить