DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: tigerleentu
今日帖子: 10
在线用户: 22
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/26 14:11:04
标题:
Delphi泛型应用示例之一:接口化指针 浏览:2140
加入我的收藏
楼主: 接口的引用计数特性可以让我们方便地创建一个接口实例,它可以到处使用而无需手动释放内存。但是假如我们这个接口里面有很多数据项那就麻烦多了,得针对每个数据项分别写出其Get和Set方法,接口要写一遍,类还要写一遍。
举个例子:
我们想把这个记录类型用接口的方式实现:
  TRecord_Test = record
    Data1: string;
    Data2: Integer;
    Data3: Double;
  end;

通常的方法是这样的:
type
  TInterface_Test = interface
    function GetData1: string;
    function GetData2: Integer;
    function GetData3: Double;
    procedure SetData1(const Value: string);
    procedure SetData2(const Value: Integer);
    procedure SetData3(const Value: Double);
    property Data1: string read GetData1 write SetData1;
    property Data2: Integer read GetData2 write SetData2;
    property Data3: Double read GetData3 write SetData3;
  end;

  TClass_Test = class(TInterfacedObject, TInterface_Test)
  private
    FData2: Integer;
    FData3: Double;
    FData1: string;
    function GetData1: string;
    function GetData2: Integer;
    function GetData3: Double;
    procedure SetData1(const Value: string);
    procedure SetData2(const Value: Integer);
    procedure SetData3(const Value: Double);
  public
    property Data1: string read GetData1 write SetData1;
    property Data2: Integer read GetData2 write SetData2;
    property Data3: Double read GetData3 write SetData3;
  end;

implementation

{ TClass_Test }
function TClass_Test.GetData1: string;
begin
  Result := FData1;
end;

function TClass_Test.GetData2: Integer;
begin
  Result := FData2;
end;

function TClass_Test.GetData3: Double;
begin
  Result := FData3;
end;

procedure TClass_Test.SetData1(const Value: string);
begin
  FData1 := Value;
end;

procedure TClass_Test.SetData2(const Value: Integer);
begin
  FData2 := Value;
end;

procedure TClass_Test.SetData3(const Value: Double);
begin
  FData3 := Value;
end;
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/26 14:11:38
1楼: 上面的代码是不是看得人吐血?要是有多个类似的记录,恐怕大家宁愿到处维护指针了。
不要紧,我们有万能的泛型。请看下述代码:
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/26 14:11:57
2楼: unit BambooInterfacedPointer;

interface

type
  TValuePointer<T> = packed record
    PValue: ^T;
  end;

  IBambooInterfacedPointer<T> = interface
    function Instence: TValuePointer<T>;
  end;

  TBambooInterfacedPointer<T> = class(TInterfacedObject, IBambooInterfacedPointer<T>)
  strict private
    FValuePointer: ^T;
  private
    function Instence: TValuePointer<T>; inline;
  public
    constructor Create;
    destructor Destroy; override;
  end;

implementation

{ TBambooInterfacedPointer<T> }

constructor TBambooInterfacedPointer<T>.Create;
begin
  inherited Create;
  New(FValuePointer);
  System.FillChar(FValuePointer^, SizeOf(T), 0);
end;

destructor TBambooInterfacedPointer<T>.Destroy;
begin
  Dispose(FValuePointer);
  inherited Destroy;
end;

function TBambooInterfacedPointer<T>.Instence: TValuePointer<T>;
begin
  Result.PValue := FValuePointer;
end;

end.
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/26 14:12:26
3楼: 说明一下,之所以要定义
type
TValuePointer<T> = packed record
PValue: ^T;
end;

这么一个东西,是因为在泛型接口里面不允许使用如下写法
IBambooInterfacedPointer<T> = interface
function Instence: ^T;
end;
所以定义了一个记录类型来作为过渡。
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/26 14:13:23
4楼: 最后,则是我写的这个接口化指针的应用实例:

type
  TRecord_Test = record
    Data1: string;
    Data2: Integer;
    Data3: Double;
  end;

var
  aInterfacedPointer: IBambooInterfacedPointer<TRecord_Test>;

begin
  aInterfacedPointer := TBambooInterfacedPointer<TRecord_Test>.Create;
  with aInterfacedPointer.Instence.PValue^ do
  begin
    Data1 := '123';
    Data2 := 456;
    Data3 := 7.89;
  end;
end;

是不是很方便啊 ? 重要的是 , 所有的数据类型都可以这么干 !
----------------------------------------------
-
作者:
男 err0rc0de (code) ▲▲▲▲△ -
普通会员
2015/2/26 14:34:51
5楼: 话说,为啥用泛型了?用指针不方便?
还是说为了封装的原因,所以要写上述一堆的代码进行get/set?

我怎么就觉得用record指针方便了?特别是上述引用的示例情况,真没想到有特需要,特合适泛型使用的场合。
----------------------------------------------
-
作者:
男 err0rc0de (code) ▲▲▲▲△ -
普通会员
2015/2/26 14:41:38
6楼: 我使用的封装顺序原则是:
1:数组+N函数
2:record+N个函数
3:1个类
4:N个类

使用的过程,特觉得指针是万能的,比泛型就是多了强制转换的过程(多了一个指针认识的过程),其它跟普通写法没区别。

另外一个原因是:今天用XE写码,明天去了另家公司,可能D5写码,那之前用泛型或高级语言特性的代码全部不能用。。。
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/26 14:56:43
7楼: 指针是万能的,维护指针是万恶的。我举的例子只是为了说明用法,不是为了证明指针接口化的优越性。代码越复杂,维护越麻烦。在多线程+数据池的情况下,我可不愿意去维护指针。
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/26 15:09:39
8楼: 至于D5什么的,我不觉得自己找不到能使用泛型的好公司。
----------------------------------------------
-
作者:
男 bigboy2050 (bigboy2050) ★☆☆☆☆ -
普通会员
2015/2/27 11:55:13
9楼: 学习ing
----------------------------------------------
kittyapp
作者:
男 dahaixingchen (dahaixingchen) ▲▲▲▲△ -
普通会员
2015/2/27 22:04:36
10楼: 学习中,楼主能否再添加上一个应用实例
----------------------------------------------
m
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/28 8:43:17
11楼: 4楼已经有例子了
----------------------------------------------
-
作者:
男 tintin1943 (零输好) ★☆☆☆☆ -
盒子活跃会员
2015/2/28 10:18:51
12楼: 支持楼主,,,DELPHI 智能指针也是根据接口计数器和泛型来实现的!
----------------------------------------------
不喧哗 自有声 心静 思远 志行千里
作者:
男 tintin1943 (零输好) ★☆☆☆☆ -
盒子活跃会员
2015/2/28 11:15:18
13楼: 楼主,,偶对 FillChar 有点担心,简单类型还行,带有生命周期的对象,可能会有内存泄漏,不知道你测试过没(比如循环1000次)。


  New(FValuePointer);
  System.FillChar(FValuePointer^, SizeOf(T), 0);
----------------------------------------------
不喧哗 自有声 心静 思远 志行千里
作者:
男 souledge (souledge) ★☆☆☆☆ -
普通会员
2015/2/28 11:23:24
13楼: 友情提示,Instence拼写错误,应为Instance
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/28 11:30:01
14楼: 正是因为考虑到带有生命周期的对象,所以才在New以后用0填充。在4楼的例子里面有string,没有发生内存泄漏。你可以设置System.ReportMemoryLeaksOnShutdown := True;来测试。
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/28 11:31:30
15楼: 谢谢souledge的提醒!
----------------------------------------------
-
作者:
男 kkkmmm (KKKMMM) ★☆☆☆☆ -
普通会员
2015/2/28 11:41:51
14楼: Instence是Instance的笔误吗?
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/28 11:49:37
16楼: to kkkmmm:
是的,笔误。
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/28 12:24:18
17楼: 内存泄漏不会发生在FillChar的时候,但是有另一种内存泄漏的可能性,那就是T里面又包含了需要手动释放的数据,比如TObject或者Pointer。比如:
type
  TRecord_Test = record
    Data1: string;
    Data2: Integer;
    Data3: Double;
    Data4: Pointer;
  end;
如果你对Data4执行了GetMem,在FreeMem之前就结束了接口的生命周期,那么会发生内存泄漏。
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/28 12:26:51
18楼: 为了避免这种未内存泄漏,我们可以给TBambooInterfacedPointer加一个方法在Dispose之前检测T里面未释放的内存:

unit BambooInterfacedPointer;

interface

uses
  System.SysUtils;

type
  TValuePointer<T> = packed record
    PValue: ^T;
  end;

  IBambooInterfacedPointer<T> = interface
    function Instance: TValuePointer<T>;
  end;

  TBambooInterfacedPointer<T> = class(TInterfacedObject, IBambooInterfacedPointer<T>)
  strict private
    FValuePointer: ^T;
    FProc_BeforeDispose: TProc<TValuePointer<T>>;
  private
    function Instance: TValuePointer<T>; inline;
  public
    constructor Create(const aProc_BeforeDispose: TProc < TValuePointer < T >> = nil);
    destructor Destroy; override;
  end;

implementation

{ TBambooInterfacedPointer<T> }

constructor TBambooInterfacedPointer<T>.Create(const aProc_BeforeDispose: TProc < TValuePointer < T >> );
begin
  inherited Create;
  FProc_BeforeDispose := aProc_BeforeDispose;
  New(FValuePointer);
  System.FillChar(FValuePointer^, SizeOf(T), 0);
end;

destructor TBambooInterfacedPointer<T>.Destroy;
begin
  if Assigned(FProc_BeforeDispose) then
    FProc_BeforeDispose(Instance);
  Dispose(FValuePointer);
  inherited Destroy;
end;

function TBambooInterfacedPointer<T>.Instance: TValuePointer<T>;
begin
  Result.PValue := FValuePointer;
end;

end.
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2015/2/28 12:30:21
19楼: 相应的例子如下(假定Data4都是通过GetMem初始化的):
type
  TRecord_Test = record
    Data1: string;
    Data2: Integer;
    Data3: Double;
    Data4: Pointer;
  end;

procedure TForm3.Button1Click(Sender: TObject);
var
  aInterfacedPointer: IBambooInterfacedPointer<TRecord_Test>;
  i: Integer;
begin
  for i := 1 to 100000 do
  begin
    aInterfacedPointer := TBambooInterfacedPointer<TRecord_Test>.Create(
      procedure(aValuePointer: TValuePointer<TRecord_Test>)
      begin
        with aValuePointer.PValue^ do
          if Assigned(Data4) then
          FreeMem(Data4);
      end);
    with aInterfacedPointer.Instance.PValue^ do
    begin
      Data1 := '123';
      Data2 := 456;
      Data3 := 7.89;
      GetMem(Data4, 1024);
    end;
  end;
end;
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行70.3125毫秒 RSS