Модераторы: Poseidon, Snowy, bems, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как объекту отказаться себя создавать? Вернуть nil - не получилось создаться 
:(
    Опции темы
Ibragim
Дата 4.10.2004, 01:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 177
Регистрация: 28.9.2004
Где: Киев

Репутация: нет
Всего: нет



Всем ночи доброй.
Такая ситуация: объект класса TMyClass сам решает при создании, есть ли подходящие условия для создания себя. Нужно, чтобы пользователь вызывал Create, а получал в ответ nil - и при этом не засорялась память и т.п. То есть хочется сделать так:

constructor TMyClass.Create
begin
<...>
if <нехочу создаваться> then
self := nil;
<...>
end

Как это правильно сделать, чтобы объект не создавался? Чтобы не выделялась память под переменные, и т.д.
Заранее спасибо.

PM MAIL   Вверх
ДЫМ
Дата 4.10.2004, 01:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 224
Регистрация: 6.1.2004

Репутация: 8
Всего: 8



Попробуй сгенерировать "тихое исключение"
if <нехочу создаваться> then Abort;
PM MAIL WWW   Вверх
<Spawn>
Дата 4.10.2004, 05:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Око кары:)
****


Профиль
Группа: Экс. модератор
Сообщений: 2776
Регистрация: 29.1.2003
Где: Екатеринбург

Репутация: 44
Всего: 64



Ibragim Примерно так:

Код
EMyException=class(Exception);

...

constructor TMyClass.Create(...);
begin
 ....
 if <какое то условие> then
   raise EMyException.Create("Исключение из конструктора");
end;


try
 MyClass = TMyClass.Create(...);
except
 on E: EMyException do
   ShowMessage(E.Message);
end;





--------------------
"Для некоторых людей программирование является такой же внутренней потребностью, подобно тому, как коровы дают молоко, или писатели стремятся писать" - Николай Безруков.
PM MAIL ICQ   Вверх
Ibragim
Дата 4.10.2004, 05:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 177
Регистрация: 28.9.2004
Где: Киев

Репутация: нет
Всего: нет



Понятно... Будем пробовать. А так чтобы

MyObject : TMyClass;
begin
MyObject := TMyClass.Create;
if MyObjec = nil then ....


никак сделать нельзя?

Моим классом будут пользоваться другие, и невозможность создать объект класса у меня не исключение, а довольно стандартная ситуация. Предполагается, что пользователь моего класса обязательно проверит - создалось что-то или нет. И если просто возвращать nil вместо указателя на класс было бы самое оно. Но если нет - что ж, будем делать как советовали.

В любом случае большое спасибо.
PM MAIL   Вверх
p0s0l
Дата 4.10.2004, 12:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Г-н Посол
****


Профиль
Группа: Экс. модератор
Сообщений: 3668
Регистрация: 13.7.2003
Где: 58°38' с.ш. 4 9°41' в.д.

Репутация: 58
Всего: 112



Можно сделать так:
Код
type
 TMyClass = class (TObject)
 protected
   AbortConstruction : boolean;
 public
   procedure AfterConstruction; override;
   constructor Create (Condition : boolean);
 end;

constructor TMyClass.Create;
begin
 inherited Create;
// ...
 if Condition then AbortConstruction := True;
// ...
end;

procedure TMyClass.AfterConstruction;
begin
 if AbortConstruction then
 begin
   Free;
   asm xor esi, esi end;
 end;
end;
В конце конструктора насильно и всегда вызывается AfterConstruction, поэтому если прямо в конструкторе сделать Free или Self := nil, то получится Access Violation. Поэтому Free делаем в AfterConstruction, а также обнуляем регистр ESI - в нём хранится Self (эквивалент Self := nil, если бы мы его делали не в AfterConstruction, а в Create)...
Пример:
Код
var
 c : TMyClass;
begin
 c := TMyClass.Create(false); // c <> nil
 c := TMyClass.Create(true); // c = nil
end;

Цитата
Как это правильно сделать, чтобы объект не создавался?
Предложенный способ не совсем правильный sad.gif


Это сообщение отредактировал(а) p0s0l - 4.10.2004, 12:07


--------------------
С уважением, г-н Посол.
PM   Вверх
Ibragim
Дата 4.10.2004, 14:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 177
Регистрация: 28.9.2004
Где: Киев

Репутация: нет
Всего: нет



Спасибо. А что значит "предложенный способ не совсем правильный"? Какой способ? Твой? Тогда как совсем правильно?
PM MAIL   Вверх
p0s0l
Дата 4.10.2004, 15:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Г-н Посол
****


Профиль
Группа: Экс. модератор
Сообщений: 3668
Регистрация: 13.7.2003
Где: 58°38' с.ш. 4 9°41' в.д.

Репутация: 58
Всего: 112



Ну да... То, что я предложил - не совсем правильно, потому что делается xor esi, esi... Это типа обмана компилятора.

Щас в голову пришёл более правильный способ (вернее, в нём уже ничего неправильного не будет):
Код

type
 TMyClass = class
 protected
   AbortConstruction : boolean;
   constructor HiddenCreate (Condition : boolean);
 public
   class function Create (Condition : boolean) : TMyClass;
 end;

class function TMyClass.Create;
begin
 Result := TMyClass.HiddenCreate(Condition);
 if Result.AbortConstruction then
 begin
   Result.Free;
   Result := nil;
 end;
end;

constructor TMyClass.HiddenCreate;
begin
 inherited Create;
// ...
 if not Condition then
 begin
   AbortConstruction := true;
   Exit;
 end;
// ...
end;

// пример:
procedure TForm1.Button3Click(Sender: TObject);
var c : TMyClass;
begin
 c := TMyClass.Create(false); // c = nil
 c := TMyClass.Create(true); // c <> nil
end;




--------------------
С уважением, г-н Посол.
PM   Вверх
<Spawn>
Дата 4.10.2004, 16:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Око кары:)
****


Профиль
Группа: Экс. модератор
Сообщений: 2776
Регистрация: 29.1.2003
Где: Екатеринбург

Репутация: 44
Всего: 64



Кстати, интересный вопрос, вот такой код показывает что указатель при моем примере не инициализируется:

Код
try
   t := TTest.Create;//В TTest.Create вызываем injerited Create; raise Exception.Create('');
 except
   ShowMessage(IntToStr(DWORD(t)));
 end;


Но вот вопрос, а все ли данные от TObject корректно высвобождаются? Ни где про это не встречал описание, а всегда вызывал исключение в случае надобности, но правильно ли этоsmile.gif Только сейчас меня эти мысли посетилиsmile.gif

p0s0l Ну тут даже и class function не нужно делатьsmile.gif Это статический метод -> он все равно всегда при жизни приложения будет в памяти. Хотя для выделения принадлежности к какому то классу может это и есть хорошо. Но вот что мне в этом подходе не нравиться - а если в конструкторе будет выделено пару десятков\сотен метров памяти(например, для работы с изображениями\видео\получение больших наборов данных от СУБД и т.д), то это будет не совсем эффективно.


--------------------
"Для некоторых людей программирование является такой же внутренней потребностью, подобно тому, как коровы дают молоко, или писатели стремятся писать" - Николай Безруков.
PM MAIL ICQ   Вверх
p0s0l
Дата 4.10.2004, 18:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Г-н Посол
****


Профиль
Группа: Экс. модератор
Сообщений: 3668
Регистрация: 13.7.2003
Где: 58°38' с.ш. 4 9°41' в.д.

Репутация: 58
Всего: 112



Цитата
Ну тут даже и class function не нужно делать Это статический метод -> он все равно всегда при жизни приложения будет в памяти.
А разве обычные методы класса не всегда в памяти ? Динамически подгружаются/выгружаются что ли ? smile.gif
Если я в программе использую TBitmap, то в .exe (и в памяти) тоже будет код конструктора TBitmap.Create.
Или я неправильно тебя понял... Объясни подробнее...

Цитата
Хотя для выделения принадлежности к какому то классу может это и есть хорошо.
Эта и есть причина. Конечно, можно было сделать отдельную функцию, типа такого:
Код
function CreateMyClass : TMyClass;
begin
 Result := TMyClass.Create;
end;
Но это как-то некрасиво. Уже все привыкли делать
c := TMyClass.Create
а не
c := CreateMyClass;

Тем более, что если применить приведённый подход, то никакой разницы ни для программиста, ни для юзера не будет. Ты разве почуствуешь разницу, Create - это конструктор или функция ? smile.gif Я лично не почувствую. Если принесут мне модуль, где описан класс TMyClass, то я на автомате напишу
c := TMyClass.Create(..), и даже не задумаюсь, конструктор это или функция...
И при таком подходе возможно то, чего хочет Ibragim:
MyObject := TMyClass.Create;
if MyObject = nil then ....

Цитата
Но вот что мне в этом подходе не нравиться - а если в конструкторе будет выделено пару десятков\сотен метров памяти(например, для работы с изображениями\видео\получение больших наборов данных от СУБД и т.д), то это будет не совсем эффективно.
Это уже не от подхода зависит, а от программиста, и от подхода не зависит...

Сравни 2 варианта:
Код
constructor TMyClass.Create;
begin
...
 <выделение 1 ГБ памяти>
...
 if <какое то условие> then
 begin
    < освобождение 1 ГБ памяти >;
   raise EMyException.Create("Исключение из конструктора");
 end
...
end;

и второй вариант:
Код
constructor TMyClass.HiddenCreate;
begin
...
 <выделение 1 ГБ памяти>
...
if <какое то условие> then
 begin    
    < освобождение 1 ГБ памяти >;
    AbortConstruction := true;
    Exit;
 end;
...
end;
В чём разница ? Не вижу никаких отличий... Отличие моего от твоего метода в том, что в первом случае генерится исключение, во втором случае - делается Exit.
Чтобы окончательно развеять твои сомнения, можно заменить все эти манипуляции с AbortConstruction и Exit теми же исключениями, и ловить эти исключения в класс-функции Create...
Тогда это будет выглядеть так:
Код
type
 TMyClass = class
 protected
   constructor HiddenCreate (Condition : boolean);
 public
   class function Create (Condition : boolean) : TMyClass;
 end;

class function TMyClass.Create;
begin
 try
   Result := TMyClass.HiddenCreate(Condition);
 except
   Result := nil;
 end;
end;

constructor TMyClass.HiddenCreate;
begin
 inherited Create;
// ...
 if not Condition then
   raise Exception.Create ('Error');
// ...
end;


Цитата
Но вот вопрос, а все ли данные от TObject корректно высвобождаются? Ни где про это не встречал описание, а всегда вызывал исключение в случае надобности, но правильно ли это Только сейчас меня эти мысли посетили
Если в конструкторе произойдёт исключение, то объект автоматически уничтожится (вызовется Destroy)...




--------------------
С уважением, г-н Посол.
PM   Вверх
Ibragim
Дата 4.10.2004, 19:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 177
Регистрация: 28.9.2004
Где: Киев

Репутация: нет
Всего: нет



Спасибо за интересные рассказы. В моем случае тормозов не будет - объемы выделения памяти не те. Хотя в контексте интересно.
Тогда Гуру - хотите еще вопрос? Совсем сложный?
Я даже и не спрашивал - но вдруг и так можно...

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

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

Для пользователя все должно выглядеть так:
MyObject: TBaseClass;
MyObject := TBaseClass.Create;

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

Это сообщение отредактировал(а) Ibragim - 4.10.2004, 19:23
PM MAIL   Вверх
p0s0l
Дата 4.10.2004, 22:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Г-н Посол
****


Профиль
Группа: Экс. модератор
Сообщений: 3668
Регистрация: 13.7.2003
Где: 58°38' с.ш. 4 9°41' в.д.

Репутация: 58
Всего: 112



Проще было бы сделать так:
Код
function SmartCreate (s : string) : TBaseClass;
begin
 if (s = 'abc') then Result := TClass1.Create
 else if (s = 'ceffvsdf') then Result := TClass2.Create
// и т.д.
end;


Если же идти таким способом, как ты сказал, то всё равно надо делать что-то для того, чтобы знать, какие возможные классы можно создать... Имхо, легче всего это сделать, если объявить массив, в котором будут указаны всевозможные наследники.

Пример. Есть такое дерево:
Код

           /----> TClass1 ----> TChild1OfClass1
TBaseClass +
           \----> TClass2 ----> TChild1OfClass2
                          \---> TChild2OfClass2

1 и 2 критерии будут проверяться функцией CheckString. Эта функция переписывается в каждом новом наследнике от TBaseClass и от его потомков.
3 и 4 критерии будут осуществляться в TBaseClass.

Т.к. constructor TBaseClass сам по себе создаёт экземпляр TBaseClass (а нам надо создать наследника от него), то вместо конструктора надо использовать функцию/класс-функцию.
И опять же, т.к. для 2 критерия надо проверять для каждого класса истинность условия, то придётся эту функцию проверки делать класс-функцией. Обычным методом сделать нельзя, т.к. самого экземпляра класса не существует еще (буди что временно создавать, но это криво).
Код
type
 TBaseClass = class
 protected
   constructor HiddenCreate;
 public
   class function CheckString (const s : string) : boolean; virtual; abstract;
   class function Create(const s : string) : TBaseClass;
 end;

 TClass1 = class(TBaseClass)
   class function CheckString (const s : string) : boolean; override;
 end;

// и т.д.

class function TClass1.CheckString;
begin
 Result := ( s = 'aaa' );
end;

class function TChild1OfClass1.CheckString;
begin
 Result := ( s = 'aaa' );
end;

// и т.д. (в других классах-наследниках, понятно, другие условия)

// Массив классовых указателей:
type TChild = class of TBaseClass;
const Childs : array [0..4] of TChild =
     (TClass1, TClass2, TChild1OfClass1, TChild2OfClass1, TChild1OfClass2);

// TBaseClass
constructor TBaseClass.HiddenCreate;
begin
 inherited Create;
end;

class function TBaseClass.Create;
var
 i, lvl, maxlvl : integer;
 best : TChild;

begin
 maxlvl := 0;
 best := nil;
 for i := 0 to High(Childs) do
   if Childs[i].CheckString(s) then
     if CountGenerations(TBaseClass, Childs[i]) > maxlvl then
     begin
       maxlvl := CountGenerations(TBaseClass, Childs[i]);
       best := Childs[i];
     end;
 if best <> nil then Result := best.HiddenCreate
   else Result := nil;
end;


Теперь если сделать так:
Код
var
 MyObject : TBaseClass;
begin
 MyObject := TBaseClass.Create('aaa');
 Caption := MyObject.ClassName;
end;
То MyObject будет TChild1OfClass1...

Есть способ без всяких массивов и других специальных прибамбасов. Только он опять же будет не совсем правильный...
И к тому же, в любом случае, кроме объявления и реализации класса, надо делать ссылку на него (например, в глобальной переменной), иначе этот класс не будет участвовать в компиляции...



--------------------
С уважением, г-н Посол.
PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0536 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.