Delphi字串的引用計數與生命週期

天地弦發表於2011-10-20

先來段程式碼

 

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;

 

 

 


相關文章