Возможность переопределять TObject.NewInstance в ее текущем виде совершенно бесполезна, потому что при этом нет возможности ни передать туда какой-либо аргумент, ни получить тип результата, отличный от TObject. Выделить память на стеке внутри NewInstance нельзя, потому что такая модификация стека сделает невозможным возврат из процедуры.
Единственный работающий вариант собственного распределения памяти для классов - фабрика, выделяющая нужные блоки памяти и вызывающая сначала InitInstance, потом конструктор. При этом запретить создавать те же объекты стандартным способом нельзя, потому что конструктор всегда public.
Поля - процедурные переменные
Модератор: Модераторы
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
-
MageSlayer
- постоялец
- Сообщения: 216
- Зарегистрирован: 07.09.2006 12:30:44
Sergei I. Gorelkin писал(а):Возможность переопределять TObject.NewInstance в ее текущем виде совершенно бесполезна, потому что при этом нет возможности ни передать туда какой-либо аргумент, ни получить тип результата, отличный от TObject.
Непонятно зачем ей что-то передавать? Отнаследовал новый тип, переопределил NewInstance и порядок. Функция-то классовая - считай глобальная. Пускай читает глобальные переменные.
Sergei I. Gorelkin писал(а):Выделить память на стеке внутри NewInstance нельзя, потому что такая модификация стека сделает невозможным возврат из процедуры.
Зачем же внутри NewInstance? Надо снаружи, в самой верхней по стеке функции. Выделить раз, и нарезать ее, при необходимости делая fallback на стандартный getmem.
Sergei I. Gorelkin писал(а):Единственный работающий вариант собственного распределения памяти для классов - фабрика, выделяющая нужные блоки памяти и вызывающая сначала InitInstance, потом конструктор. При этом запретить создавать те же объекты стандартным способом нельзя, потому что конструктор всегда public.
Как-то слишком категорично.
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
MageSlayer писал(а):Непонятно зачем ей что-то передавать? Отнаследовал новый тип, переопределил NewInstance и порядок. Функция-то классовая - считай глобальная. Пускай читает глобальные переменные.
С глобальными переменными очень тяжело создавать один и тот же тип объектов, беря память поочередно от разных менеджеров памяти (из разных блоков). Передать информацию о блоке в конструктор можно без проблем, а в NewInstance - никак.
MageSlayer писал(а):Как-то слишком категорично.
Решал я задачу управлением пулом объектов, получил массу удовольствия, потому и категорично.
-
MageSlayer
- постоялец
- Сообщения: 216
- Зарегистрирован: 07.09.2006 12:30:44
Sergei I. Gorelkin писал(а):MageSlayer писал(а):Непонятно зачем ей что-то передавать? Отнаследовал новый тип, переопределил NewInstance и порядок. Функция-то классовая - считай глобальная. Пускай читает глобальные переменные.
С глобальными переменными очень тяжело создавать один и тот же тип объектов, беря память поочередно от разных менеджеров памяти (из разных блоков). Передать информацию о блоке в конструктор можно без проблем, а в NewInstance - никак.MageSlayer писал(а):Как-то слишком категорично.
Решал я задачу управлением пулом объектов, получил массу удовольствия, потому и категорично.
Э-э. Вроде задача решается даже в общем виде. Допускаю, что чего-то не понимаю.
Грубо, по типу следующего:
Код: Выделить всё
program project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes
{ you can add units after this };
{$R *.res}
type
TFakeClass = class
class function newinstance : tobject;override;
end;
class function TFakeClass.newinstance : tobject;
begin
//эмуляция нестандартной кучи
getmem(Pointer(Result), InstanceSize);
if Result <> nil then
InitInstance(Result);
end;
function CloneClass(C:TClass;var Vmt:TVmt):TClass;
begin
Move( (PPointer(C))^, Vmt, SizeOf(Vmt) );
Vmt.vNewInstance:=@TFakeClass.newinstance;
Result:=TClass(@Vmt);
end;
type
{ TClass1 }
TClass1 = class
private
FStr:String;
public
constructor Create;
end;
TMetaClass1 = class of TClass1;
{ TClass1 }
constructor TClass1.Create;
begin
FStr:='Test';
end;
var O:TClass1;
OMeta:TMetaClass1;
V:TVmt;
begin
OMeta:=TMetaClass1(CloneClass(TClass1,V));
O:=OMeta.Create;
Writeln(O.FStr);
O.Free;
end.
... ну, и собственно, нужно просто наделать несколько переменных метаклассов и реализаций TFakeClass.
- Sergei I. Gorelkin
- энтузиаст
- Сообщения: 1409
- Зарегистрирован: 24.07.2005 14:40:41
- Откуда: Зеленоград
Ага, TVmt - полезный тип
Чтобы не умереть от скромности, скажу, что он появился в RTL как раз в результате моих изысканий 
Только с ним есть небольшая проблема: TVmt является не структурой класса целиком, а только "заголовком", за которым следуют адреса виртуальных методов. А общий размер этой конструкции нигде не хранится. Так что предложенное "клонирование" сработает только для классов без виртуальных методов.
А вот что получается, если не лезть в NewInstance и создавать объекты с помощью фабрики:
...почувствуйте разницу.
Только с ним есть небольшая проблема: TVmt является не структурой класса целиком, а только "заголовком", за которым следуют адреса виртуальных методов. А общий размер этой конструкции нигде не хранится. Так что предложенное "клонирование" сработает только для классов без виртуальных методов.
А вот что получается, если не лезть в NewInstance и создавать объекты с помощью фабрики:
Код: Выделить всё
procedure TFactory.Alloc(aClass: TSomeClass): TObject;
begin
result := CustomGetMemory(aClass.InstanceSize);
aClass.InitInstance(result);
result.Create;
end;
...почувствуйте разницу.
-
MageSlayer
- постоялец
- Сообщения: 216
- Зарегистрирован: 07.09.2006 12:30:44
Sergei I. Gorelkin писал(а):Только с ним есть небольшая проблема: TVmt является не структурой класса целиком, а только "заголовком", за которым следуют адреса виртуальных методов. А общий размер этой конструкции нигде не хранится. Так что предложенное "клонирование" сработает только для классов без виртуальных методов.
Жаль, очень жаль.
Sergei I. Gorelkin писал(а):А вот что получается, если не лезть в NewInstance и создавать объекты с помощью фабрики:Код: Выделить всё
procedure TFactory.Alloc(aClass: TSomeClass): TObject;
begin
result := CustomGetMemory(aClass.InstanceSize);
aClass.InitInstance(result);
result.Create;
end;
...почувствуйте разницу.
Ну, это слишком просто
