![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Ипользование объекта вместо записи
atruhin © (2006-09-03 12:26:00)Иногда удобно вместо записи использовать объект, например:
Вместо:
record X,Y : integer; end;
Использовать:
class
X, Y : integer;
constructor Create(aX,aY : integer);
end;
Вопрос: какова разница в объеме занимаемой памяти? Т.е. таблицы VMT, доп. информация? И влияет ли на разницу наличие constructor
Джо (2006-09-03 23:47:00) [21]Я как-то для хранения большого количества однотипных объектов использовал (для уменьшения расходов памяти) следующий подход. Внутренне данные хранились в приватном поле класса в динамическом массиве. А наружу торчал аксессор, на лету преобразующий элементы массива в класс, точнее, интерфейс.
Вот упрощенный и несколько схематичный пример, основанный на реальном коде. :) Кажется, есть какой-то "паттерн" такой, забыл только название.
unit Points;
interface
uses SysUtils;
type
IPoint = interface
['{245732B1-B9AF-415D-9269-8DC9ACA8A1D3}']
function GetX: Double;
function GetY: Double;
procedure SetX (const Value: Double);
procedure SetY (const Value: Double);
property X: Double read GetX write SetX;
property Y: Double read GetY write SetY;
// ...
// ...
end;
TPoints = class; // forward declaration
TPoint = record
X,Y: Double;
end;
TPointArray = array of TPoint;
TPoints = class
private
FPointArray: TPointArray; // Массив записей, а не классов!
function GetPoints(Index: Integer): IPoint;
procedure SetPoints(Index: Integer; const Value: IPoint);
public
property Points[Index: Integer]: IPoint read GetPoints write SetPoints; default;
function AddPoint (const X,Y: Double): Integer;
// ...
// ...
end;
implementation
type
TPointClass = class (TInterfacedObject, IPoint)
private
FParent: TPoints;
FParentIndex: Integer;
FX,
FY: Double;
function GetX: Double;
function GetY: Double;
procedure SetX (const Value: Double);
procedure SetY (const Value: Double);
procedure NotifyParent;
public
constructor Create (AParent: TPoints; const FParentIndex: Integer;
const X,Y: Double);
end;
constructor TPointClass.Create(AParent: TPoints; const FParentIndex: Integer;
const X,Y: Double);
begin
inherited Create;
FX := X;
FY := Y;
FParent := AParent;
end;
function TPointClass.GetX: Double;
begin
Result := FX
end;
function TPointClass.GetY: Double;
begin
Result := FY
end;
procedure TPointClass.NotifyParent;
begin
if Assigned(FParent) then
FParent[FParentIndex] := Self
end;
procedure TPointClass.SetX(const Value: Double);
begin
FX := Value;
NotifyParent
end;
procedure TPointClass.SetY(const Value: Double);
begin
FY := Value;
NotifyParent
end;
function TPoints.AddPoint(const X, Y: Double): Integer;
begin
Inc (FCount);
if FCount > FCapacity then
Reallocate;
FPointArray[FCount-1].X := X;
FPointArray[FCount-1].Y := Y;
Result := FCount-1;
end;
// "На лету" преобразует элементы внутреннего массива
// в интерфейс
function TPoints.GetPoints(Index: Integer): IPoint;
begin
Result := TPointClass.Create(Self, Index,
FPointArray[Index].X,FPointArray[Index].Y);
end;
procedure TPoints.SetPoints(Index: Integer; const Value: IPoint);
begin
FPointArray[Index].X := Value.X;
FPointArray[Index].Y := Value.Y;
end;
end.
default (2006-09-03 18:32:00) [22]цель, как я понимаю, недопустить использование неинициализированных данных
хорошая цель
например, CLR дотнетовская воспринимает обращение к неинициализированных данным(обычно они находятся в стеке) как код не проходящий верификацию:) то есть в безопасной среде - такой код исполнен не будет
так что шаги на пути к безопасному коду я только поощряю:)
tesseract (2006-09-03 18:23:00) [23]> [4] Джо © (03.09.06 14:02)
Ну и плюс с приведением типов проблемы будут. Лучше уж запись.
default (2006-09-03 18:16:00) [24]цель замена какая?
sniknik (2006-09-03 14:59:00) [25]> аА у object накладные расходы на служебную информацию (VMT), а именно их и хотят, по возможности, избежать ;)
не очень красиво, но ... пример в D7
type
TMet = record
x, y: integer;
Show: TNotifyEvent;
end;
PMet = ^TMet;
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
Met: TMet;
procedure MetShow(Sender: TObject);
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.MetShow(Sender: TObject);
var
LocMet: PMet;
begin
LocMet:= PMet(Pointer(Self));
ShowMessage(IntToStr(LocMet.x + LocMet.y));
end;
procedure TForm1.FormCreate(Sender: TObject);
procedure SetMethod(val: TNotifyEvent);
begin
TMethod(Met.Show).Code:= TMethod(val).Code;
TMethod(Met.Show).Data:= @Met;
end;
begin
SetMethod(MetShow);
Met.x:= 10;
Met.y:= 15;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Met.Show(Self);
end;
и удобным это тоже не назовеш... но накладные расходы на VMT избегаются.
ну еще лучше бы вынести всю обработку рекорда в отдельный модуль, тогда чтобы не привязыватся к форме обработчики делать процедурами "of object", но это частности ткак сказать, зачем автору это нужно неясно, может как раз таких/похожих "замоморочек" хочет избежать... а тут, это. ;о)
atruhin (2006-09-03 14:54:00) [26]> Размер VMT для разных версий Delphi тоже немного разный,
> но где-то примерно около сотни байт
Да но ведь VMT хранится для класса а не для экземпляра объекта? Тогда это не принципиально.
А вопрос вообще возникает в случае хранения тысяч объектов в памяти, например в TList и т.д., в таких случаях удобнее работать с объектами, а не записями. Т.е. насколько я понял для экземпляра объекта добавляется всего 4 байта, по сравнению с записью?
Джо (2006-09-03 14:02:00) [27]> [3] Palladin ©
А у object накладные расходы на служебную информацию (VMT), а именно их и хотят, по возможности, избежать ;)
Palladin (2006-09-03 13:46:00) [28]Или использовать, чуть более широкий по возможностям чем Record, Object. В котором позволены методы со времен TP5.5 :)
Джо (2006-09-03 13:35:00) [29]> ИМХО, можно просто создать процедуру типа CreateMyRecord
> - и тогда овчинка вряд ли стоит выделки.
Или использовать компилятор BDS 2006 с новым синтаксисом записей, в котором позволены методы в записях.
Юрий Зотов (2006-09-03 12:59:00) [30]Сам объект занимает на 4 байта больше аналогичной записи (по нулеврму смещению он содержит неявное поле - ссылку на VMT). Плюс хотя бы одна ссылка на этот объект (он же не может, как запись, располагаться в статической памяти) - еще 4 байта. Размер VMT для разных версий Delphi тоже немного разный, но где-то примерно около сотни байт (конкретно можно посмотреть в System.pas). Плюс таблица полей, таблица свойств, таблица методов... их размер зависит от количества этих самых полей, свойств и методов в секции published (если компиляция идет с $M+). В общем, минимальная разница - примерно байт сто, но может быть и больше.
Влияет ли на разницу наличие constructor - если этот конструктор имеет код, то где-то этот код тоже ведь должен храниться? Он и хранится, в теле самой программы. Плюс небольшие накладные расходы на код вызова конструктора и деструктора.
ИМХО, можно просто создать процедуру типа CreateMyRecord - и тогда овчинка вряд ли стоит выделки.