Delphi 字符串的引用计数与生命周期

简介: 先来段代码   type  MyString = AnsiString;  PMyChar = PAnsiChar;procedure TForm2.Button2Click(Sender: TObject);var  p: PMyChar;  s, s2: MyString;begin  self.

先来段代码

 

type
  MyString = AnsiString;
  PMyChar = PAnsiChar;


procedure TForm2.Button2Click(Sender: TObject);
var
  p: PMyChar;
  s, s2: MyString;
begin
  self.Caption :=  ' frmTest ';  // 7位的字符串
  p := GetCaption;
  s2 := p;   //这是时候s2 为frmTes
  ShowMessage(s2);  //*****显示出来为frmTes
end;

function TForm2.GetCaption: PMyChar;
var
  s1, s2: MyString;
begin
  s2 := MyString(self.Caption);
  Result := PMyChar(MyString(s2));
end;

 

研究说明(代表个人意见)(XE下面测试)

 

function TForm2.GetCaption: PMyChar;
var
  s1: MyString;
begin
  s1 := MyString(Self.Caption);    //self.Caption源码得知,是获取了一块临时的空间(A1)
  //A1(integer(s1))

  i:= StringRefCount(s1);  //i= 1

  Result := PMyChar(MyString(s1));  //Result指针指向的为(A1)的空间
  //Integer(@Result^) = A1(integer(s1))  是指向同一块空间

  i:= StringRefCount(s1);  //i= 1
end;
//函数返回后s1因为是局部变量 s1的引用计数为0,integer(s1)的空间被标志为可以覆盖
//返回的为指针,不增加s1的引用计数


procedure TForm2.Button2Click(Sender: TObject);
var
  p: PMyChar;
  s, s2: MyString;
begin
  self.Caption :=  ' frmTest ';
  p := GetCaption; //实际上p指向的那块地址被标注为可以覆盖,随时都有可能被覆盖,是很危险的
  //Integer(@p^) = GetCaption内部给s1分配的那块空间地址
 
  s2 := p;         //导致丢掉了字符..
  //因为p指向的内存是可以被覆盖的,s2分配的地址可能和p指向的地址是一样的,导致丢掉了字符..
  //Integer(s2) 可能= Integer(@p^)  测试是发现都一样

  //下面操作(SetLength)同样也会一样结果,s2占用的和p占用的同样大小(或者小)。
  //这样导致了s2分配的空间可能和p内存一样
  //如果7改成较大的数就正常
  //  SetLength(s2, 7);
  //  StrCopy(PMyChar(s2), p);

  ShowMessage(s2);  //错误
end;

 

 解决方案(1)

将s1定义为内成员变量,这样当GetCaption执行完后那块空间不会被标准为可读写

 

function TForm2.GetCaption2: PMyChar;
begin
  FMyCaption := MyString(Self.Caption);    //self.Caption源码得知,是获取了一块临时的空间(A1)
  //A1(integer(FMyCaption))

  i:= StringRefCount(FMyCaption);  //i= 1

  Result := PMyChar(MyString(FMyCaption));  //Result指针指向的为(A1)的空间
  //Integer(@Result^) = A1(integer(FMyCaption))  是指向同一块空间

  i:= StringRefCount(FMyCaption);  //i= 1
end;
//执行完后FMyCaption不是临时变量,指向的地址不可以被覆盖

procedure TForm2.btnGetCaption2Click(Sender: TObject);
var
  p: PMyChar;
  s, s2: MyString;
begin
  self.Caption :=  ' frmTest ';
  p := GetCaption2; //实际上p指向的那块地址和FMyCaption的地址是一样的
  //Integer(@p^) = GetCaption2内部给FMyCaption分配的那块空间地址是一样

  s2 := p;
  //因为p指向的内存是不可以被覆盖的,s2分配的地址不可可能和p指向的地址是一样的,这样做是安全的
  //Integer(s2) <> Integer(@p^)

  i:= StringRefCount(s2);  //i= 1  新的内存

  ShowMessage(s2);  //正确
end;

 

 

***

局部变量

 

function TForm2.GetCaption2: PMyChar;
var

  s1: MyString;
begin
  s1 :=  ' frmTest '
  i:= StringRefCount(s1);  //i=- 1  常量地址指向空间不可被覆盖
 
  //UniqueString(s1); 
  //i:= StringRefCount(s1);  //i= 1  s1又变成临时的,函数返回后s1指向的地址不再安全
 
  Result=PMyChar(s1)
end;

 

 

 

目录
相关文章
|
6月前
|
Java Python
当引用计数器为0时,Python是如何确定哪些对象需要被回收的?
【2月更文挑战第19天】【2月更文挑战第56篇】当引用计数器为0时,Python是如何确定哪些对象需要被回收的?
55 3
|
5月前
|
算法 Java 程序员
Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。
【6月更文挑战第20天】Python内存管理用引用计数(对象的`ob_refcnt`)跟踪对象,但循环引用(如A-&gt;B-&gt;A)可导致内存泄漏。为解决此问题,Python使用`gc`模块检测并清理循环引用,可通过`gc.collect()`手动回收。此外,Python结合标记清除和分代回收策略,针对不同生命周期的对象优化垃圾回收效率,确保内存有效释放。
43 3
|
4月前
|
算法 Java Python
分代回收在Python中是如何解决循环引用问题的?
【7月更文挑战第2天】分代回收在Python中是如何解决循环引用问题的?
35 0
|
6月前
|
存储 安全 Java
Python中的引用和赋值机制允许变量引用内存中的对象,并通过引用计数来管理对象的生命周期
【5月更文挑战第14天】Python中的变量是对象引用,不存储数据,而是在内存中创建对象。赋值操作创建新变量并使其指向已有对象。引用计数用于管理对象生命周期,引用数为0时对象被回收。理解这些机制对编写高效Python代码很重要。
63 6
|
6月前
|
安全 编译器 C++
C++类与对象【对象的初始化和清理】
C++类与对象【对象的初始化和清理】
C++类与对象【对象的初始化和清理】
|
6月前
|
存储 程序员 编译器
c++面向对象概述、内存分析、引用、函数
c++面向对象概述、内存分析、引用、函数
|
6月前
|
算法 Java 程序员
Python内存管理:请解释Python中的引用计数机制以及如何处理循环引用。描述一下Python是如何通过垃圾回收来释放不再使用的对象内存的。
Python内存管理:请解释Python中的引用计数机制以及如何处理循环引用。描述一下Python是如何通过垃圾回收来释放不再使用的对象内存的。
106 1
|
存储 Python
【Python】栈堆内存之引用的秘密
【Python】栈堆内存之引用的秘密
|
存储 缓存 算法
JVM之对象创建流程及对象内存布局
JVM之对象创建流程及对象内存布局
138 0
JVM之对象创建流程及对象内存布局
|
Java C++
对象“变形记”——初识引用与GC | 带你学《Java面向对象编程》之三
作为Java世界的核心内容,类与对象凭借其引用数据类型的内在特质,实现了引用传递的能力,为整个Java世界添上了浓墨重彩的一笔。