主题:内存的管理(第二节) DATE :2004-09-24

主题:内存的管理(第二节)
时间:9月24日下午3点
主持:A3.武稀松
地点:会议室

2004-09-24 15:02:49 A3.武稀松
好!开课

2004-09-24 15:03:31 英雄莫问出处
老兄,先列个提纲

2004-09-24 15:03:38 A3.武稀松
要讲内存管理显要知道Delphi的数据类型分几种?

2004-09-24 15:03:57 黄
喜欢,

2004-09-24 15:04:02 英雄莫问出处
这个恐怕不要讲了吧?

2004-09-24 15:04:00 B6
基本的吧

2004-09-24 15:04:21 欢乐狗熊
似乎有很多种哦,呵呵

2004-09-24 15:04:22 英雄莫问出处
不是说要讲共享内存的几种方式吗?

2004-09-24 15:04:23 B6
integer,char,^

2004-09-24 15:04:30 B6
record

2004-09-24 15:04:42 B6
string

2004-09-24 15:04:56 飘尘
还有CLASS了

2004-09-24 15:05:06 关艳月
其实,数据就分两种,实型和虚型。

2004-09-24 15:06:13 关艳月
象 pchar ,string, class ,record,array 就是虚型。

2004-09-24 15:06:17 关艳月
对不?

2004-09-24 15:06:24 欢乐狗熊
武松又不说话了

2004-09-24 15:06:25 英雄莫问出处
没错

2004-09-24 15:06:31 飘尘
string 应当是

CHAR的扩展类型

2004-09-24 15:06:40 欢乐狗熊

2004-09-24 15:06:48 A3.武稀松
一种是值类型,一种是抽象数据类型

2004-09-24 15:06:56 英雄莫问出处
等到真正定义虚型变量的时候,才分配内存

2004-09-24 15:07:13 B6
重点是内存管理,大家别跑题了

2004-09-24 15:07:36 英雄莫问出处
不是上次已经讲过基本的了?

2004-09-24 15:07:43 A3.武稀松
浮点数、有序数、纪录等都是抽象数据类型

2004-09-24 15:07:58 A3.武稀松
这种类型的内存管理基本我们不用管它

2004-09-24 15:08:03 关艳月
内存的基本类型要认识,这也是内存管理的重要因素啊。

2004-09-24 15:08:37 英雄莫问出处
这次讲讲共享内存的几种方式,比如内存映象文件、线程共享模式等?

2004-09-24 15:08:51 A3.武稀松
关艳月说得对,数据类型先搞清楚才能搞清楚内存管理

2004-09-24 15:09:32 A3.武稀松
另外一种抽象数据类型就是我们要手工分配长度的,例如指针、对象等等

2004-09-24 15:10:14 关艳月
我的做法很简单,所有的数据就分两种,这样最容易懂了。 A1教的。[:D]

2004-09-24 15:10:38 A3.武稀松
你倒是比较会活学活用

2004-09-24 15:11:28 A3.武稀松
其中值类型是由栈来管理的。这样我们就只要关心抽象数据类型就可以了

2004-09-24 15:11:54 た少林足球
这些对我来说太抽象了,听起来不太懂

2004-09-24 15:12:10 A3.武稀松
上次A1讲的银行卡就是我们这次话题的基础

2004-09-24 15:12:29 C03.MUSIC
有没有必要讲讲堆和栈

2004-09-24 15:12:45 た少林足球
那就讲一下

2004-09-24 15:12:49 关艳月
A3,不要把此“抽象”和类的“抽象”混和了,大家会混稍的。

2004-09-24 15:13:33 A3.武稀松
堆栈以后讲。其实Delphi中抽象数据类型也不一定非要我们手工分配和释放。有些编译器会帮我们处理。例如字符串

2004-09-24 15:14:07 关艳月
有两种字符串, string 和 pchar 

2004-09-24 15:14:14 A3.武稀松
谁知道字符串的生存周期是怎样的?为什么不需要我们手工干预?

2004-09-24 15:14:48 英雄莫问出处
这是在DELPHI编译器里决定的

2004-09-24 15:15:02 英雄莫问出处
会自动分配和释放,继续

2004-09-24 15:15:14 Ares
有两种字符串, string 和 pchar????

2004-09-24 15:15:15 A3.武稀松
对,但是编译器怎么知道什么时候释放?

2004-09-24 15:15:37 关艳月
你要干预它也可以啊。比如,当用到MoveMemory()的时候。

2004-09-24 15:15:52 A3.武稀松
PChar严格来说是字符指针。Type PChar=Char;

2004-09-24 15:15:59 A3.武稀松
PChar=^Char;

2004-09-24 15:16:20 Ares
movememory和copymemory有什么区别?

2004-09-24 15:16:36 A3.武稀松
字符串和接口、动态数组一样都是通过引用计数器来管理的

2004-09-24 15:17:17 A3.武稀松
引用计数器是垃圾回收中最快捷的一种方式

2004-09-24 15:18:24 关艳月
却是在DLL和EXE沟通中最可恨的方式。别怪我老挑毛病。

2004-09-24 15:18:51 A3.武稀松
也就是说在你赋值的时候他会自动把引用计数加一,表示有一个变量在使用它。当变量销毁或指向别处的时候他会自动把计数减一,如果引用计数为0。就回收这片内存

2004-09-24 15:20:00 A3.武稀松
String的引用计数器我们是不是有必要让她暴露出来看一下她的真面目?

2004-09-24 15:20:33 Ares
如果我手工将计数改了, 会有什么情况?

2004-09-24 15:20:53 关艳月
怕是你好难改吧。

2004-09-24 15:21:07 A3.武稀松
可以改。我来先讲一下字符串的构造。

2004-09-24 15:21:47 A3.武稀松
字符串其实可以看成是一个结构的指针。

2004-09-24 15:22:04 Ares
应该是@S[0] - 4

2004-09-24 15:22:21 A3.武稀松
我自己定义了一个和系统的AnsiString兼容的字符串结构。如下:
  _PTAnsiString = ^_TAnsiString;
  _TAnsiString = record
    allocSiz: LongWord;                 //分配的大小
    ReferencCount: LongWord;            //引用次数
    Length: LongWord;                   //字符串长度
    Data: array[1..(Length + 1)] of AnsiChar;
  end;

2004-09-24 15:23:26 A3.武稀松
可以看到Ares说的减4得到的是字符串的长度位置,而非今天我们要说的计数器位置

2004-09-24 15:24:08 A3.武稀松
计数器应该时减8才对

2004-09-24 15:24:24 A3.武稀松(42088303)
下面我们来证实一下

2004-09-24 15:24:46 Ares(53257667)
总之是那减去longint的长度的整倍数

2004-09-24 15:25:11 A3.武稀松(42088303)
type
  _PTAnsiString = ^_TAnsiString;
  _TAnsiString = record
    allocSiz: LongWord;                 //分配的大小
    ReferencCount: LongWord;            //引用次数
    Length: LongWord;                   //字符串长度
    //Data: array[1..(Length + 1)] of AnsiChar;
  end;
var
  k, M, N           : AnsiString;
  p                 : _PTAnsiString;

begin
  k := 'I am a Delphi fan!';
  p := Pointer(Integer(@K[1]) - SizeOf(_TAnsiString));

  Showmessage(format('编译器初始化分配给内存大小是:%d'
    + #13 + '引用次数是:%d'
    + #13 + '字符串长度是:%d;'
    + #13 + '字符串内容是:"%s"',
    [P^.allocSiz, P^.ReferencCount, P^.Length, K]));
end;

2004-09-24 15:25:19 Ares(53257667)
所以, 如果手工将那改了, 会不会有什么问题

2004-09-24 15:25:54 A3.武稀松(42088303)
大家可以试一下我上面贴的代码

2004-09-24 15:29:18 A3.武稀松(42088303)
我讲到怎么查看字符串的引用计数器

2004-09-24 15:29:33 A3.武稀松(42088303)
procedure TForm1.Button3Click(Sender: TObject);
type
  _PTAnsiString = ^_TAnsiString;
  _TAnsiString = record
    allocSiz: LongWord;                 //分配的大小
    ReferencCount: LongWord;            //引用次数
    Length: LongWord;                   //字符串长度
    //Data: array[1..(Length + 1)] of AnsiChar;
  end;
var
  k, M, N           : AnsiString;
  p                 : _PTAnsiString;

begin
  k := 'I am a Delphi fan!';
  p := Pointer(Integer(@K[1]) - SizeOf(_TAnsiString));

  Showmessage(format('编译器初始化分配给内存大小是:%d'
    + #13 + '引用次数是:%d'
    + #13 + '字符串长度是:%d;'
    + #13 + '字符串内容是:"%s"',
    [P^.allocSiz, P^.ReferencCount, P^.Length, K]));
end;

2004-09-24 15:31:41 TCP/IP INFO(31329846)
我执行了

2004-09-24 15:31:48 A3.武稀松(42088303)
OK。那我们在调整一下

2004-09-24 15:32:13 A3.武稀松(42088303)
type
  _PTAnsiString = ^_TAnsiString;
  _TAnsiString = record
    allocSiz: LongWord;                 //分配的大小
    ReferencCount: LongWord;            //引用次数
    Length: LongWord;                   //字符串长度
    //Data: array[1..(Length + 1)] of AnsiChar;
  end;
var
  k, M, N           : AnsiString;
  p                 : _PTAnsiString;

begin
  k := 'I am a Delphi fan!';
  p := Pointer(Integer(@K[1]) - SizeOf(_TAnsiString));
  M := K; //把K赋值给M实际上只是把M的指向地址修改为与K相同,并且把引用次数加一
  Showmessage(format('编译器初始化分配给内存大小是:%d'
    + #13 + '引用次数是:%d'
    + #13 + '字符串长度是:%d;'
    + #13 + '字符串内容是:"%s"',
    [P^.allocSiz, P^.ReferencCount, P^.Length, K]));
end;

2004-09-24 15:32:55 A3.武稀松(42088303)
看看有什么变化


2004-09-24 15:33:28 Ares(53257667)
type
  PStrRec = ^StrRec;
  StrRec = packed record
    refCnt: Longint;
    length: Longint;
  end;

2004-09-24 15:33:50 欢乐狗熊(228175978)
引用次数加1

2004-09-24 15:34:09 A3.武稀松(42088303)
Ares的是System单元给出的逻辑结构,不是真实的。还有些是他隐藏了的

2004-09-24 15:34:26 Ares(53257667)
不过好象ShortString不是这样的吧

2004-09-24 15:34:39 A3.武稀松(42088303)
procedure TForm1.Button3Click(Sender: TObject);
type
  _PTAnsiString = ^_TAnsiString;
  _TAnsiString = record
    allocSiz: LongWord;                 //分配的大小
    ReferencCount: LongWord;            //引用次数
    Length: LongWord;                   //字符串长度
    //Data: array[1..(Length + 1)] of AnsiChar;
  end;
var
  k, M, N           : AnsiString;
  p                 : _PTAnsiString;

begin
  k := 'I am a Delphi fan!';
  p := Pointer(Integer(@K[1]) - SizeOf(_TAnsiString));
  M := K; //把K赋值给M实际上只是把M的指向地址修改为与K相同,并且把引用次数加一
  M := M + 'A';//把M的内容修改,这时候M和K的内容不同了,就不再用同一个地址
               //而这时才为M分配一个新的内存块,把M指向新块,同时M原来的指向引用计数减一
  Showmessage(format('编译器初始化分配给内存大小是:%d'
    + #13 + '引用次数是:%d'
    + #13 + '字符串长度是:%d;'
    + #13 + '字符串内容是:"%s"',
    [P^.allocSiz, P^.ReferencCount, P^.Length, K]));
end;

2004-09-24 15:34:51 A3.武稀松(42088303)
我说了我演示的是AnsiString

2004-09-24 15:36:03 A3.武稀松(42088303)
procedure TForm1.Button3Click(Sender: TObject);
type
  _PTAnsiString = ^_TAnsiString;
  _TAnsiString = record
    allocSiz: LongWord;                 //分配的大小
    ReferencCount: LongWord;            //引用次数
    Length: LongWord;                   //字符串长度
    //Data: array[1..(Length + 1)] of AnsiChar;
  end;
var
  k, M, N           : AnsiString;
  p                 : _PTAnsiString;

begin
  k := 'I am a Delphi fan!';
  p := Pointer(Integer(@K[1]) - SizeOf(_TAnsiString));
  M := K; //把K赋值给M实际上只是把M的指向地址修改为与K相同,并且把引用次数加一
  M := M + 'A';//把M的内容修改,这时候M和K的内容不同了,就不再用同一个地址
               //而这时才为M分配一个新的内存块,把M指向新块,同时M原来的指向引用计数减一
  Showmessage(format('编译器初始化分配给内存大小是:%d'
    + #13 + '引用次数是:%d'
    + #13 + '字符串长度是:%d;'
    + #13 + '字符串内容是:"%s"',
    [P^.allocSiz, P^.ReferencCount, P^.Length, K]));
end;

2004-09-24 15:37:00 A3.武稀松(42088303)
那么大家对引用计数怎么理解的呢?

2004-09-24 15:37:45 TCP/IP INFO(31329846)
可能和 java 的 string 差不多

2004-09-24 15:37:58 TCP/IP INFO(31329846)
都是静态的吧

2004-09-24 15:38:12 TCP/IP INFO(31329846)
每次都分配

2004-09-24 15:38:17 A3.武稀松(42088303)
Java的垃圾回收比较复杂。Delphi就比较简单

2004-09-24 15:38:20 关艳月(110499998)
别用红色的字!!!

2004-09-24 15:38:24 D10.天地弦(185511468)
  _TAnsiString = record
    allocSiz: LongWord; //分配的大小
    ReferencCount: LongWord; //引用次数
    Length: LongWord; //字符串长度

2004-09-24 15:39:01 A3.武稀松(42088303)
D10这是给出的字符串逻辑结构。和系统的ANSIString兼容的

2004-09-24 15:39:42 A3.武稀松(42088303)
垃圾回收有很多种,其中引用计数是最为原始但最有效的。

2004-09-24 15:39:58 A3.武稀松(42088303)
这里的有效是指时间效率

2004-09-24 15:40:59 TCP/IP INFO(31329846)
把M的内容修改,这时候M和K的内容不同了,delphi 怎么实现的

2004-09-24 15:41:42 Ares(53257667)
把M的内容修改,这时候M和K的内容不同了,delphi 怎么实现的?
复制

2004-09-24 15:41:54 A3.武稀松(42088303)
编译器会处理:=这个动作如果内容相同的话就是用同一空间,计数加一。如果内容不同就重新分配空间,原计数器减一

2004-09-24 15:42:46 关艳月(110499998)
我提问:假如: A:='Hello';   B:=A ; C:=A+''  ,这时A的计数是多少?

2004-09-24 15:43:15 A3.武稀松(42088303)
Delphi中动态数组、字符串、接口都是通过计数器来管理的内存的。所以我们不必理会他们。

2004-09-24 15:43:30 A3.武稀松(42088303)
2

2004-09-24 15:44:08 TCP/IP INFO(31329846)
我试了是 2

2004-09-24 15:44:12 C03.MUSIC(281361369)
为什么是2不是3

2004-09-24 15:44:19 A3.武稀松(42088303)
我来解释一下

2004-09-24 15:44:45 Ares(53257667)
var
  S : string;
  P : ^longInt;
begin
  S := 'abcdefg';
  P := Pointer(integer(@S[1]) - 4);
  showmessage(IntToStr(P^));
  P^ := 0;
  ShowMessage(S);
end;
为什么显示出来是'a'而不是''

2004-09-24 15:45:20 A3.武稀松(42088303)
A:='Hello';计数器=1
B:=A ; 计数器=2
C:=A+''A所指向字符串的计数器不受影响

2004-09-24 15:45:44 关艳月(110499998)
为什么C不会影响A呢

2004-09-24 15:45:58 D10.天地弦(185511468)
C是另外一个变量了

2004-09-24 15:46:07 A3.武稀松(42088303)
D10说得对

2004-09-24 15:46:15 欢乐狗熊(228175978)
新加了一个字符重新分空间了

2004-09-24 15:46:21 A3.武稀松(42088303)
D10理解了计数器。

2004-09-24 15:46:24 TCP/IP INFO(31329846)
A+''时候,可能系统又分配了空间,让C指向

2004-09-24 15:46:32 A3.武稀松(42088303)
狗熊也明白了

2004-09-24 15:46:41 Ares(53257667)
A3, 帮我结实一下我那结果啊

2004-09-24 15:46:58 A3.武稀松(42088303)
这么多人都懂了,没白费我的口水

2004-09-24 15:48:05 关艳月(110499998)
C:=A+''   这个''是空的。

2004-09-24 15:48:20 Ares(53257667)
人蠢, 没办法。 小狗狗, 明白了吗?

2004-09-24 15:48:26 A3.武稀松(42088303)
那么我们再讲一下对象的内存管理方式

2004-09-24 15:48:50 D10.天地弦(185511468)
Ares的意思吧,为C分配了另外一个空间。不是引用

2004-09-24 15:49:08 关艳月(110499998)
从意义上来讲 C=A是成立的。

2004-09-24 15:49:50 A3.武稀松(42088303)
但是从编译器看来他不知道内容是否相同啊

2004-09-24 15:49:55 Ares(53257667)
Ares的意思吧,为C分配了另外一个空间。不是引用
我是将字符串长度设置成了0

2004-09-24 15:50:21 A3.武稀松(42088303)
这个话题可以结束了吧。有人不懂就去问D10。[:P]

2004-09-24 15:50:45 D10.天地弦(185511468)
我怕误人子弟

2004-09-24 15:51:00 TCP/IP INFO(31329846)
继续吧

2004-09-24 15:51:10 A3.武稀松(42088303)
大家说对象的内存分配是如何分配的?

2004-09-24 15:51:14 关艳月(110499998)
在DLL和EXE沟通的时候, C:=A+''是非常有意义的。

2004-09-24 15:51:37 关艳月(110499998)
继续,我不打扰了。[;-D]

2004-09-24 15:51:52 D10.天地弦(185511468)
A3是不是就说了这一个?

2004-09-24 15:51:59 D10.天地弦(185511468)
我迟到了

2004-09-24 15:53:06 A3.武稀松(42088303)
D10来的不晚刚刚说一个

2004-09-24 15:53:39 A3.武稀松(42088303)
既然来晚了就罚你说说你对对象的内存分配的理解

2004-09-24 15:54:38 D10.天地弦(185511468)
对象的内存分配。是.....

2004-09-24 15:54:49 D10.天地弦(185511468)
不懂。听课!

2004-09-24 15:55:16 A3.武稀松(42088303)
你平时怎么创建对象的呢

2004-09-24 15:55:31 Ares(53257667)
Create

2004-09-24 15:55:40 D10.天地弦(185511468)
A3 := TA3.Create

2004-09-24 15:55:43 C03.MUSIC(281361369)
好象对象的内存分配已经由DELPHI自己给完成了

2004-09-24 15:55:49 Ares(53257667)
直接调用TClass的Create

2004-09-24 15:55:59 关艳月(110499998)
严格来说。Create不能算是真正的创建。

2004-09-24 15:56:08 TCP/IP INFO(31329846)
newinstance

2004-09-24 15:56:17 B4.松鼠(64652023)
那什么时候创建?

2004-09-24 15:57:05 关艳月(110499998)
指定了Constructor的方法可能不只是Create ,但Delphi只认 Constructor的东西。

2004-09-24 15:57:58 A3.武稀松(42088303)
说得对,其实在编译的时候如果是Constructor方法,在第一个Constructor方法前会插入newinstance

2004-09-24 15:58:17 A3.武稀松(42088303)
我说的第一个是指继承层次上的最后一层

2004-09-24 15:59:06 关艳月(110499998)
学来的知识现炒现卖。[;P]

2004-09-24 16:00:40 关艳月(110499998)
继续。

2004-09-24 16:00:56 D10.天地弦(185511468)
在第一个Constructor方法前会插入newinstance
 
2004-09-24 16:01:01 A3.武稀松(42088303)

2004-09-24 16:01:26 A3.武稀松(42088303)
我们可以做一个实验

2004-09-24 16:01:28 D10.天地弦(185511468)
在第一个Constructor方法前会插入newinstance
我说的第一个是指继承层次上的最后一层
可是还是不懂。我是小学生

2004-09-24 16:01:38 A3.武稀松(42088303)

2004-09-24 16:01:45 TCP/IP INFO(31329846)
是缺省,还是...

2004-09-24 16:01:53 关艳月(110499998)
这个好象之前A1和A2有开课过了。

2004-09-24 16:02:10 A3.武稀松(42088303)
就是说在Inherited它祖先的Create方法的时候不会再调用了

2004-09-24 16:02:11 D10.天地弦(185511468)
A3做实验

2004-09-24 16:02:13 B4.松鼠(64652023)
最后一层是指TObject?

2004-09-24 16:02:48 A3.武稀松(42088303)
type
  TTest = class
  private
    FA: string;
  public
    class function NewInstance: TObject; override;
  end;

2004-09-24 16:03:28 A3.武稀松(42088303)
procedure TTest.FreeInstance;
begin
  //CleanupInstance;
  //_FreeMem(Self);
  FreeMem(Pointer(Self),InstanceSize);
end;

2004-09-24 16:03:39 A3.武稀松(42088303)
搞错了,更正

2004-09-24 16:03:48 A3.武稀松(42088303)
class function TTest.NewInstance: TObject;
var
  Instance          : Pointer;
begin
  //Result := InitInstance(_GetMem(InstanceSize));
  GetMem(Instance, InstanceSize);
  PInteger(Instance)^ := Integer(Self);
  Result := InitInstance(TObject(Instance));
end;

2004-09-24 16:04:35 A3.武稀松(42088303)
class function TTest.NewInstance: TObject;
var
  Instance          : Pointer;
begin
  //Result := InitInstance(_GetMem(InstanceSize));
  GetMem(Instance, InstanceSize);
  Result := InitInstance(TObject(Instance));
end; 

2004-09-24 16:04:42 D10.天地弦(185511468)
A3NewInstance这个方法在哪里调用?

2004-09-24 16:05:02 关艳月(110499998)
Constrcutor之前。

2004-09-24 16:05:03 A3.武稀松(42088303)
在构造函数的最前面

2004-09-24 16:05:23 D10.天地弦(185511468)
是隐藏的?

2004-09-24 16:05:33 A3.武稀松(42088303)
对,由编译器负责

2004-09-24 16:05:46 D10.天地弦(185511468)
哦,你要早说清楚

2004-09-24 16:06:44 A3.武稀松(42088303)
一旦调用过以后他会在DL寄存器上做标记。由此判断,以后的Inherited祖先的构造方法就不会再调用NewInstance了

2004-09-24 16:07:34 D10.天地弦(185511468)
FreeInstance是最后调用的方法?

2004-09-24 16:07:38 A3.武稀松(42088303)

2004-09-24 16:07:40 A3.武稀松(42088303)
class function TTest.NewInstance: TObject;
var
  Instance          : Pointer;
begin
  //Result := InitInstance(_GetMem(InstanceSize));
  GetMem(Instance, InstanceSize);
  Result := InitInstance(TObject(Instance));
end;

2004-09-24 16:07:52 A3.武稀松(42088303)
注释掉的是系统的代码。

2004-09-24 16:08:08 A3.武稀松(42088303)
我用代码解释了一下系统的代码

2004-09-24 16:09:06 C03.MUSIC(281361369)
先分配内存,再初始化实例

2004-09-24 16:09:15 A3.武稀松(42088303)
对的

2004-09-24 16:09:16 关艳月(110499998)
简单是最好的东西,不知能不能用简单的方式来说明这个东西。

2004-09-24 16:09:48 C03.MUSIC(281361369)
实例都已经初始化了,那CREATE是干什么用的?

2004-09-24 16:10:04 D10.天地弦(185511468)
做要做的事

2004-09-24 16:10:16 A3.武稀松(42088303)
InitInstance中会自动把内存全部清零。这就是为什么我们的对象中的Integer会是0,字符串会是空

2004-09-24 16:10:44 A3.武稀松(42088303)
Create主要是让你来初始化对象的

2004-09-24 16:11:05 D10.天地弦(185511468)
看看Create的源码就知道了

2004-09-24 16:11:13 Ares(53257667)
initinstance是怎么样的代码

2004-09-24 16:11:25 D10.天地弦(185511468)
var
  IntfTable: PInterfaceTable;
  ClassPtr: TClass;
  I: Integer;
begin
  FillChar(Instance^, InstanceSize, 0);
  PInteger(Instance)^ := Integer(Self);
  ClassPtr := Self;
  while ClassPtr <> nil do
  begin
    IntfTable := ClassPtr.GetInterfaceTable;
    if IntfTable <> nil then
      for I := 0 to IntfTable.EntryCount-1 do
  with IntfTable.Entries[I] do
  begin
    if VTable <> nil then
      PInteger(@PChar(Instance)[IOffset])^ := Integer(VTable);
  end;
    ClassPtr := ClassPtr.ClassParent;
  end;
  Result := Instance;

2004-09-24 16:11:51 A3.武稀松(42088303)
谢谢,我不用找代码了
FillChar(Instance^, InstanceSize, 0);就是我说的那一句清零动作


2004-09-24 16:12:21 Ares(53257667)
FillMemory还是fillchar

2004-09-24 16:12:32 A3.武稀松(42088303)
效果一样

2004-09-24 16:12:41 Ares(53257667)
fillchar能用0来做参数吗

2004-09-24 16:13:09 Ares(53257667)
错了, 确实是0

2004-09-24 16:13:34 ★hotdog★(278136300)
0是不是有特殊的含义阿

2004-09-24 16:13:52 Ares(53257667)
fillmemory是不是内部有循环的代码啊

2004-09-24 16:14:05 D10.天地弦(185511468)
插一句小学生的问题构造函数可以不用Create是吗?
construtor selfcreate()也可以是吗?

2004-09-24 16:14:09 A3.武稀松(42088303)
PInteger(Instance)^ := Integer(Self);实例的类的信息其实

2004-09-24 16:14:24 C03.MUSIC(281361369)
TO D10:
  可以

2004-09-24 16:14:26 关艳月(110499998)
to D10,小学生也知道可以。

2004-09-24 16:14:36 A3.武稀松(42088303)
构造方法名字随便他只认construtor关键字

2004-09-24 16:15:00 Ares(53257667)
我现在想知道TClass中类型信息放在什么地方

2004-09-24 16:15:00 D10.天地弦(185511468)
呵呵,我是只学了两个月的学前班。A1说的。所以不要见怪。

2004-09-24 16:15:31 D10.天地弦(185511468)
  TClass = class of TObject;

2004-09-24 16:15:32 A3.武稀松(42088303)
while ClassPtr <> nil do
  begin
    IntfTable := ClassPtr.GetInterfaceTable;
    if IntfTable <> nil then
      for I := 0 to IntfTable.EntryCount-1 do
  with IntfTable.Entries[I] do
  begin
    if VTable <> nil then
      PInteger(@PChar(Instance)[IOffset])^ := Integer(VTable);
  end;
这一段是实现对象和接口的绑定

2004-09-24 16:15:34 Ares(53257667)
A3, 说说这方面的吧, 你说的哪内存分配没意思

2004-09-24 16:16:54 A3.武稀松(42088303)
其实我们都研究到了这种地步内存分配也没什么神秘的了

2004-09-24 16:17:08 D10.天地弦(185511468)
没有神秘好啊

2004-09-24 16:17:31 C03.MUSIC(281361369)
DELPHI的内存分配代码是不是汇编实现的

2004-09-24 16:18:07 A3.武稀松(42088303)
其实内存管理还有一点,万变不离其宗只要不是API分配的内存,不管什么方法都回落到GetMem上

2004-09-24 16:19:06 A3.武稀松(42088303)
New,AllocMem不管怎么样最后都要落在GetMem上。API除外


2004-09-24 16:20:08 关艳月(110499998)
A3,我建议换个方式来讲。[:)]

2004-09-24 16:20:09 A3.武稀松(42088303)
如果时间还够的话可以讲内存管理器。

2004-09-24 16:20:20 D10.天地弦(185511468)
好好,讲内存管理器

2004-09-24 16:20:21 A3.武稀松(42088303)
关艳月什么方式呢?

2004-09-24 16:20:46 关艳月(110499998)
比如,从技巧上来讲。

2004-09-24 16:21:01 关艳月(110499998)
例如,

2004-09-24 16:21:54 A3.武稀松(42088303)
我觉得有了这些较为基础的知识,技巧只是思考和经验的结果。这些基础的知识是本,技巧是末。不知关艳月如何认为?

2004-09-24 16:22:13 关艳月(110499998)
A是一个64KB的字符串,B要取得A前面的32KB,什么方式最好?也是内存管理问题。

2004-09-24 16:22:13 D10.天地弦(185511468)
A3讲内存管理器吧

2004-09-24 16:22:34 Ares(53257667)
A是一个64KB的字符串,B要取得A前面的32KB,什么方式最好?也是内存管理问题。
说说

2004-09-24 16:23:03 A3.武稀松(42088303)
我不想解决这些问题。我们可以以后私聊解决

2004-09-24 16:23:22 A3.武稀松(42088303)
我就讲内存管理器的知识了

2004-09-24 16:23:41 关艳月(110499998)
那好,我再问一个内存管理器的问题。

2004-09-24 16:24:06 A3.武稀松(42088303)
能开Delphi的就打开System单元,我们对照着讲比较方便

2004-09-24 16:24:28 A3.武稀松(42088303)
其实这里比较简单。

2004-09-24 16:24:58 A3.武稀松(42088303)
看这里
  PMemoryManager = ^TMemoryManager;
  TMemoryManager = record
    GetMem: function(Size: Integer): Pointer;
    FreeMem: function(P: Pointer): Integer;
    ReallocMem: function(P: Pointer; Size: Integer): Pointer;
  end;

2004-09-24 16:25:06 关艳月(110499998)
如何改造一个内存管理器,使得让Object的所申请的内存是全局的。

2004-09-24 16:25:27 D10.天地弦(185511468)
这个属于和A3私聊的问题

2004-09-24 16:26:12 A3.武稀松(42088303)
系统会创建一个默认的内存管理器。只要不是API分配内存,就一定会落在这个内存管理器的GetMem方法上

2004-09-24 16:26:41 D10.天地弦(185511468)
在什么时候创建

2004-09-24 16:27:03 A3.武稀松(42088303)
var
  MemoryManager: TMemoryManager = (
    GetMem: SysGetMem;
    FreeMem: SysFreeMem;
    ReallocMem: SysReallocMem);
这是系统默认的内存管理器

2004-09-24 16:27:35 A3.武稀松(42088303)
全局变量可以直接付初始值

2004-09-24 16:27:54 D10.天地弦(185511468)
举例

2004-09-24 16:28:04 A3.武稀松(42088303)
例如

2004-09-24 16:29:11 A3.武稀松(42088303)
当我们创建一个对象的时候刚才我们说了NewInstance里面用到了_GetMem,所以也落到了MemoryManager的GetMem方法上了

2004-09-24 16:29:43 A3.武稀松(42088303)
如果我们自己接管了MemoryManager,我们就可以知道全部VCL的内存分配和释放了

2004-09-24 16:29:46 Ares(53257667)
全局变量可以直接付初始值?
var
  Form1: TForm1 = TForm1.Create(nil);
怎么通过不了?

2004-09-24 16:30:10 D10.天地弦(185511468)
没有老爸

2004-09-24 16:30:24 D10.天地弦(185511468)
怎么创建Form

2004-09-24 16:30:32 D10.天地弦(185511468)
你要掉在空中啊

2004-09-24 16:31:14 A3.武稀松(42088303)
procedure GetMemoryManager(var MemMgr: TMemoryManager);
procedure SetMemoryManager(const MemMgr: TMemoryManager);
function IsMemoryManagerSet: Boolean;
看到了吧

2004-09-24 16:31:34 D10.天地弦(185511468)
OK

2004-09-24 16:32:14 C06.文(4184443)
procedure GetMemoryManager(var MemMgr: TMemoryManager);
begin
  MemMgr := MemoryManager;
end;
???

2004-09-24 16:32:23 A3.武稀松(42088303)
我们可以用GetMemoryManager取道系统默认的内存管理器,用SetMemoryManager分配我们自己的管理器。

2004-09-24 16:33:05 D10.天地弦(185511468)
全局函数

2004-09-24 16:33:35 D10.天地弦(185511468)
MemoryManager是个全局变量吧

2004-09-24 16:33:45 A3.武稀松(42088303)
这样我们就可以监视VCL中的内存分配和销毁了。很多VCL的内存泄漏监视工具都是用接管MemoryManager来实现的

2004-09-24 16:33:56 D10.天地弦(185511468)
直接使用不就行了,还要用GetMemoryManager来取?

2004-09-24 16:34:53 A3.武稀松(42088303)
你调用完了你自己的GetMem在最后一行最好调用一下原有的管理器的Getmem

2004-09-24 16:35:16 A3.武稀松(42088303)
有点类似于Hook技术

2004-09-24 16:36:10 A3.武稀松(42088303)
Hook,钩子也,接管原有功能。

2004-09-24 16:36:18 C06.文(4184443)
建议哪位老大有空可以讲讲!

2004-09-24 16:37:03 TCP/IP INFO(31329846)
继续讲呀

2004-09-24 16:37:08 A3.武稀松(42088303)
我只是把一些概念介绍给大家。具体如何灵活的的糟蹋,蹂躏Delphi的内存还要大加利用这些知识自己去捉摸

2004-09-24 16:38:39 Ares(53257667)
A3, 给我们讲讲VMT吧

2004-09-24 16:38:55 D10.天地弦(185511468)
A3这个讲完了?

2004-09-24 16:38:55 A3.武稀松(42088303)
所以我不想决绝问题,只是把最基本的概念介绍一下。以后大家自己发挥了。
遗漏的和错误的概念等A1兄回来补充。
下课啦[v]

2004-09-24 16:38:57 英雄莫问出处(28922297)
虚拟方法表

2004-09-24 16:38:58 C03.MUSIC(281361369)
内存管理器就这些吗?

2004-09-24 16:39:13   ~云淡风轻~(151367466)
虚拟方法表我懂

2004-09-24 16:39:16   ~云淡风轻~(151367466)
哈哈

2004-09-24 16:39:21 C06.文(4184443)
那你说说!

2004-09-24 16:39:23 英雄莫问出处(28922297)
今天到此为止?

2004-09-24 16:39:24 Ares(53257667)
赶快去嘘嘘

2004-09-24 16:39:26 B3.Locet(2212967)
那你也说说VMT~~云妹妹`

2004-09-24 16:39:49   ~云淡风轻~(151367466)
自己看书
 
2004-09-24 16:39:54 B3.Locet(2212967)
看样子你掌握的不够

2004-09-24 16:39:55   ~云淡风轻~(151367466)
开发人员指南里面有
 
2004-09-24 16:40:00   ~云淡风轻~(151367466)
就几句

2004-09-24 16:40:35 ★hotdog★(278136300)
指针和对象是需要程序员手工申请和释放的内存。

指针包括PChar、Pointer(无类型指针)、记录指针、变量指针(指向原子变量)、函数指针(例如回调函数,分为全局函数和对象方法)。使用New函数来申请内存,使用Dispose来释放指针。另外,GetMem、ReallocMem和FreeMem也是一系列申请、释放内存的函数,可以通过GetMemoryManager和SetMemoryManager函数来读取和设置Delphi的三个内存管理函数。
 
2004-09-24 16:41:00 Ares(53257667)
inside VCL是李维的书吧

2004-09-24 16:42:51 D10.天地弦(185511468)
A3回来没有


2004-09-24 16:45:40 A3.武稀松(42088303)
福州人很讨厌 ?地方歧视吧

2004-09-24 16:45:41 D10.天地弦(185511468)
不是

2004-09-24 16:47:10 D10.天地弦(185511468)
还有两个问题等你回答

2004-09-24 16:48:07 D10.天地弦(185511468)
185511468(D10.天地弦) 16:35:01
MemoryManager是个全局变量吧
直接使用不就行了,还要用GetMemoryManager来取? 


2004-09-24 16:48:58 C03.MUSIC(281361369)
NOT BBB = PPP

2004-09-24 16:49:31 D10.天地弦(185511468)
A3

2004-09-24 16:51:12 A3.武稀松(42088303)
他既然这么提供我们就这么用呗。那Borland没规定你的构造函数必须要用Create我们咋都用Create命名呢

2004-09-24 16:51:26 D10.天地弦(185511468)
哦。

[闲聊中....]

2004-09-24 16:55:42 A1.Aleyn.wu(45198124)
DLL和EXE,开始了。

2004-09-24 16:55:58 A1.Aleyn.wu(45198124)
跟内存管理有关。

2004-09-24 16:56:49 A1.Aleyn.wu(45198124)
头痛的事情好多,我先放松一下,给大家一个小技巧。

2004-09-24 16:57:38 A1.Aleyn.wu(45198124)
大家可以一起做个实验。

2004-09-24 16:59:14 A1.Aleyn.wu(45198124)
在EXE中,var a:string

2004-09-24 16:59:31 A1.Aleyn.wu(45198124)
然后 a:='hello';

2004-09-24 16:59:53 A1.Aleyn.wu(45198124)
在DLL中,b:=a

2004-09-24 17:00:01 A1.Aleyn.wu(45198124)
并showmessage(b)

2004-09-24 17:00:35 TCP/IP INFO(31329846)
我试过,不行,可能有问题

2004-09-24 17:00:40 A3.武稀松(42088303)
a要是全局变量应该正常吧

2004-09-24 17:00:57 飘尘(43745429)
也不对吧

2004-09-24 17:01:11 A1.Aleyn.wu(45198124)
从最简单的来,A假设是全局的。

2004-09-24 17:01:11 飘尘(43745429)
要是有两个程序在调用A了

2004-09-24 17:01:39 A3.武稀松(42088303)
A1有没有试过a是全局变量吧

2004-09-24 17:01:49 A1.Aleyn.wu(45198124)
呵呵,都有试过。

2004-09-24 17:02:00 A3.武稀松(42088303)
和局部变量一样?

2004-09-24 17:02:06 飘尘(43745429)
写两个程序

2004-09-24 17:02:23 飘尘(43745429)
一起调用那个DLL

2004-09-24 17:02:37 A1.Aleyn.wu(45198124)
这不是最重要的问题。

2004-09-24 17:02:43 Ares(53257667)
使用了ShareMem单元没

2004-09-24 17:02:52 A1.Aleyn.wu(45198124)
不使用。

2004-09-24 17:03:11 Ares(53257667)
不使用恐怕不是很好吧

2004-09-24 17:03:19 A3.武稀松(42088303)
我最喜欢DLL和EXE都带包与行了,啥麻烦事都没有了

2004-09-24 17:03:40 A1.Aleyn.wu(45198124)
使用了就不谈今天的这个技巧了。

2004-09-24 17:04:10 A3.武稀松(42088303)
看来比较有意思,看A1笑得那么开心

2004-09-24 17:04:59 A1.Aleyn.wu(45198124)
上面是正常的。

2004-09-24 17:05:14 D10.天地弦(185511468)
出错是正常的?

2004-09-24 17:05:15 A1.Aleyn.wu(45198124)
我们要做的不是这些,这只是测试。

2004-09-24 17:06:00 A1.Aleyn.wu(45198124)
再来,我们在DLL中,a:='hello'+' who are you'

2004-09-24 17:06:29 A1.Aleyn.wu(45198124)
再到EXE中。ShowMessage(a),这时,正常吗?

2004-09-24 17:07:16 A3.武稀松(42088303)
按我说的技巧,EXE和DLL都带包。就啥问题都没有了

2004-09-24 17:07:55 A1.Aleyn.wu(45198124)
A3兄,不是每个程序都要做成包的型式,要不尾巴太大了。

2004-09-24 17:08:19 D10.天地弦(185511468)
A3你包全带?

2004-09-24 17:08:34 A3.武稀松(42088303)
可是如果EXE和DLL共享包总体积反而会小

2004-09-24 17:08:44 Ares(53257667)
按我说的技巧,EXE和DLL都带包。就啥问题都没有了
还不如全部用包算了

2004-09-24 17:09:05 A3.武稀松(42088303)
只要带VCL,RTL包就可以了

2004-09-24 17:09:17 A1.Aleyn.wu(45198124)
算了,还是找个时间写个DEMO。要不难讲。

2004-09-24 17:10:05 D10.天地弦(185511468)
A1我帮你写好了一部分Demo

2004-09-24 17:10:08 A3.武稀松(42088303)
DLL和EXE之间用String本来就是危险的嘛

2004-09-24 17:10:23 D10.天地弦(185511468)
A1我测试怎么没有问题

2004-09-24 17:10:31 D10.天地弦(185511468)
var
  a: string;
  DLLHandle: Thandle;
  DLLSub: procedure(Astr: string);
begin
  a := 'Delphi';
  DLLhandle := LoadLibrary('ADLL.DLL');
  @DLLSub := GetProcAddress(DLLHandle, 'TestDLL');
  DLLSub(a);

2004-09-24 17:10:34 Ares(53257667)
DLL和EXE之间用String本来就是危险的嘛
我们没有传任何string

2004-09-24 17:10:40 D10.天地弦(185511468)
没有错误出现

2004-09-24 17:12:51 A1.Aleyn.wu(45198124)
to D10, 我写个DEMO,你就知道错误在什么地方出现了,就象上次的DEMO一样。

[没有正事说了,A1 Demoing 其他人谈论怎么泡MM中...]

posted @ 2004-09-25 12:04  D10.天地弦  阅读(720)  评论(0编辑  收藏  举报