3の場合のプログラムの組み方が分かりません。
下記例では対象オブジェクト内に逆参照用のポインタを用意しましたが、そもそも破
棄されたオブジェクトから逆参照するのは問題あるということは分かります。
どなたか分かる方よろしくお願いします。もちろんこの例ではオブジェクトがnilか
どうか見ればよいだけですが、そうも行かない場合に直面してます。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TItem = class(TObject)
public
name:string;
resPointer:Pointer;
end;
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
item:TItem;
itemPointer:Pointer;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); begin
item:=TItem.Create;
item.name:='test';
new(itemPointer);
item.resPointer:=itemPointer;
itemPointer:=Pointer(item);
end;
procedure TForm1.FormDestroy(Sender: TObject); begin
Button1Click(Sender);
end;
procedure TForm1.Button1Click(Sender: TObject); begin
if item<>nil then
begin
item.Free;
item.resPointer:=nil;
item:=nil;
end;
end;
procedure TForm1.Button2Click(Sender: TObject); begin
if TItem(itemPointer).resPointer=nil then
caption:='ない'
else
caption:=TItem(itemPointer).name;
end;
end.
赤尾 鉄平
"Teppei Akao" <tet...@med.hokudai.ac.jp>さん:
TObjectListでインスタンスを管理するというのはダメなんでしょうか?
ただ、インスタンスが別のところで破棄されたら。。と言う問題は残りそうですが。
> もちろんこの例ではオブジェクトがnilか
> どうか見ればよいだけですが、そうも行かない場合に直面してます。
オブジェクト変数がnilかどうかと、その参照先のインスタンスが生きているかどう
かは別だと思いますが。
■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■
◇ 諸農和岳 <k-mo...@mbm.nifty.com>
■ Kazutaka Morono - Osaka , Japan
◇
■ Microsoft MVP for Visual Developer C# (Oct 2004 - Sept 2007)
◇ with VAIO PCG-X505/SP EXTREME
■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■◇■
諸農さま
> TObjectListでインスタンスを管理するというのはダメなんでしょうか?
> ただ、インスタンスが別のところで破棄されたら。。と言う問題は残りそうです
が。
今回管理したいのはTObjectは直接アクセスできて自分自身削除することができま
す。
このようなTObjectを複数管理しており、それぞれのTObject群が別のTObject群とリ
ンクを張るようにポインタ参照しております。
当初、自分自身削除機能は不要かと思っていたのですが、やはりそれも有りというこ
とにしたため、このような事態になりました。
> オブジェクト変数がnilかどうかと、その参照先のインスタンスが生きているかど
う
> かは別だと思いますが。
正におっしゃる通りです。
今回は「参照先のインスタンスが生きているか」が知りたいのです
例
オブジェクトA群(ポインタ参照先)
A-a(C-b)
A-b(C-a)
A-c(C-c)
オブジェクトB群
B-a(C-c)
B-b(C-b)
B-c(C-b)
オブジェクトC群
C-a
C-b
C-c
オブジェクトC-cが自分自身もしくは何れのオブジェクトから削除された、
C-c自身は自分がどこから参照されているか分からない
しばらくしてA-cもしくはB-aがC-cを参照しようとしたところxxx
こんな感じです。
/////////////////////////////////////
TItem = class(TObject)
public
name:string;
end;
item:TItem;
itemPointer:Pointer;
/////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
begin
item:=TItem.Create;
item.name:='test';
new(itemPointer);
itemPointer:=Pointer(item);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Button1Click(Sender);
Dispose(itemPointer);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if item<>nil then
begin
item.Free;
item:=nil;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if TItem(itemPointer)=存在する then //ここが知りたい
caption:=TItem(itemPointer).name;
else
caption:='ない'
end;
赤尾さん:
> 正におっしゃる通りです。
> 今回は「参照先のインスタンスが生きているか」が知りたいのです
私は普段、オブジェクトの生死を管理する場所(オブジェクトなど)を明確に
するよう心がけていますが、時にはそれができない場合もあります。 その場合、
当該オブジェクトのコンストラクタに参照変数へのポインタを渡し、
デストラクタで参照変数をリセットします。
type TCustomItem=class(TObject)
constructor Create(ReferencePtr:Pointer);
destructor Destroy; override;
private
FReferencePtr:Pointer;
...
end;
constructor TCustomItem.Create;
begin
inherited;
FReferencePtr:=ReferencePtr;
end;
destructor TCustomItem.Destroy;
begin
TObject(FReferencePtr^):=nil;
inherited;
end;
この方法だと、確実に参照変数がリセットされますので便利です。
FreeOnTerminate=True で使われるスレッドクラスを作る時などにも、
多用するテクニックです。
以上、ご参考になれば幸いです。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
電子メール:tarou_...@imageom.co.jp
ホームページ:http://www.imageom.co.jp/
高木さま、
> 私は普段、オブジェクトの生死を管理する場所(オブジェクトなど)を明
> 確にするよう心がけていますが、時にはそれができない場合もあります。
> その場合、当該オブジェクトのコンストラクタに参照変数へのポインタを渡
> し、デストラクタで参照変数をリセットします。
やはり、オブジェクトを破棄を管理しないのはお行儀が悪いのですかねぇ…
教えて頂いた方法だと、親は一つに限定されてしまい、2通目に返信した形式への対
応が困難に思えました、しかしこんな方法もあるということ勉強になりました。
今回はややメモリを食いますが、やはり参照先、参照元をしっかり管理する基本クラ
スを作成し、そこから継承させることにしました、
幸いデータの扱いはTObjectからしか継承していないのでTObjectと既にできているク
ラスの間にこのクラスを挟むことで解決しそうです。
もしお時間に余裕がある方がいらっしゃったら添削していただけると幸いです。
Unit1.pas //サンプルソース
hFeNetLinkObject.pas //オブジェクト(多対多)のリンクを管理するTObject派生クラ
ス
Unit1.pas
//---------------------------------------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, hFeNetLinkObject, StdCtrls;
type
TNetLinkObject = class(TFeNetLinkObject)
public
Name : String;
end;
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
Net:array [0..4] of TNetLinkObject;
procedure ListUp;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); var i:Integer; begin
for i:=0 to Length(Net)-1 do
begin
Net[i]:=TNetLinkObject.Create;
Net[i].Name:='Net['+inttostr(i)+']';
end;
Randomize;
for i:=0 to 30 do
Net[Random(4)].AddLinkTo(Net[Random(4)]);
ListUp;
end;
procedure TForm1.ListUp;
var i,p:Integer;
LO:TNetLinkObject;
begin
ListBox1.Clear;
for i:=0 to Length(Net)-1 do
begin
if Net[i]<>nil then
begin
for p:=0 to Net[i].LinkToCount-1 do
begin
LO:= TNetLinkObject(Net[i].LinkToObject(p));
if LO=nil then
ListBox1.Items.Add(Net[i].Name + ' -> '+ 'リンク切れ')
else
ListBox1.Items.Add(Net[i].Name + ' -> '+ LO.Name);
end;
end else begin
ListBox1.Items.Add('Net['+inttostr(i)+']は破棄されています');
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject); begin
ListUp;
end;
procedure TForm1.Button2Click(Sender: TObject); var
aNet:TNetLinkObject;
begin
aNet:= Net[StrtoInt(Edit1.Text)];
if aNet<>nil then
begin
Net[StrtoInt(Edit1.Text)].Free;
Net[StrtoInt(Edit1.Text)]:=nil;
end;
ListUp;
end;
end.
//---------------------------------------------
hFeNetLinkObject.pas
//---------------------------------------------
unit hFeNetLinkObject;
interface
uses
Windows, SysUtils, Classes;
type
TFeNetLinkObject = class(TObject)
private
fLinkToList:TList;
fLinkFromList:TList;
procedure AddLinkFrom(FeNetLinkObject:TFeNetLinkObject);
procedure LinkedObjectDestroyed(FeNetLinkObject:TFeNetLinkObject);
public
constructor Create;
destructor Destroy; override;
function LinkToCount:Integer;
function LinkToObject(Index:Integer):TFeNetLinkObject;
procedure AddLinkTo(FeNetLinkObject:TFeNetLinkObject);
end;
implementation
procedure ListClear(aList: TList);
var i:Integer;
begin
for i:=0 to aList.Count-1 do
aList.Items[i]:=nil;
aList.Clear;
end;
{ TFeNetLinkObject }
procedure TFeNetLinkObject.AddLinkFrom(FeNetLinkObject: TFeNetLinkObject);
begin
fLinkFromList.Add(Pointer(FeNetLinkObject));
end;
procedure TFeNetLinkObject.AddLinkTo(FeNetLinkObject: TFeNetLinkObject); var
pt:Pointer; begin
pt:=Pointer(FeNetLinkObject);
fLinkToList.Add(pt);
TFeNetLinkObject(pt).AddLinkFrom(self);
end;
constructor TFeNetLinkObject.Create;
begin
fLinkToList :=TList.Create;
fLinkFromList:=TList.Create;
end;
destructor TFeNetLinkObject.Destroy;
var
i:Integer;
FeNetLinkObject: TFeNetLinkObject;
begin
for i:=0 to fLinkFromList.Count-1 do
begin
if fLinkFromList.Items[i]<>nil then
TFeNetLinkObject(fLinkFromList.Items[i]).LinkedObjectDestroyed(self);
end;
ListClear(fLinkToList);
ListClear(fLinkFromList);
fLinkToList.Free;
fLinkFromList.Free;
inherited;
end;
procedure TFeNetLinkObject.LinkedObjectDestroyed(FeNetLinkObject:
TFeNetLinkObject); var
pt:Pointer;
i:Integer;
begin
pt:=Pointer(FeNetLinkObject);
for i:=0 to fLinkToList.Count-1 do
begin
if pt=fLinkToList.Items[i] then
begin
fLinkToList.Items[i]:=nil;
exit;
end;
end;
end;
function TFeNetLinkObject.LinkToCount: Integer; begin
Result:=fLinkToList.Count;
end;
function TFeNetLinkObject.LinkToObject(Index: Integer): TFeNetLinkObject;
begin
if fLinkToList.Items[Index]=nil then
Result:=nil else
Result:=TFeNetLinkObject(fLinkToList.Items[Index]);
end;
end.
赤尾さん:
>> 私は普段、オブジェクトの生死を管理する場所(オブジェクトなど)を明
>> 確にするよう心がけていますが、時にはそれができない場合もあります。
>> その場合、当該オブジェクトのコンストラクタに参照変数へのポインタを渡
>> し、デストラクタで参照変数をリセットします。
> やはり、オブジェクトを破棄を管理しないのはお行儀が悪いのですかねぇ…
そう思います。 インスタンスが破棄された後は、何がどうなっていても
文句は言えないので、破棄されない部分で管理してやらないといけません。
> 教えて頂いた方法だと、親は一つに限定されてしまい、2通目に返信した
> 形式への対応が困難に思えました
その場合には参照ポインタのリストを引数にすれば大丈夫ですよ。