Ada 程式設計/屬性/'Unrestricted Access
Unrestricted_Access 屬性類似於 Access,但省略了所有可訪問性和別名檢視檢查。這是一個使用者自擔風險的屬性。
對於物件,它類似於 Address,在需要訪問型別的值時,它是 Address 的理想替代。換句話說,它的效果類似於首先應用 Address 屬性,然後將其未經檢查地轉換為所需的訪問型別。
對於子程式,可以在 P'Access 非法的情況下使用 P'Unrestricted_Access,以構造一個指向巢狀程度更高的子程式的巢狀程度較低的命名訪問型別的值。只要巢狀程度更高的子程式仍然存在,此值就可以用於間接呼叫;一旦包含它的子程式返回,此類呼叫就會出錯。例如
package body P is
type Less_Nested is not null access procedure;
Global : Less_Nested;
procedure P1 is
begin
Global.all;
end P1;
procedure P2 is
Local_Var : Integer;
procedure More_Nested is
begin
... Local_Var ...
end More_Nested;
begin
Global := More_Nested'Unrestricted_Access;
P1;
end P2;
end P;
當從 P2 呼叫 P1 時,透過 Global 的呼叫是正常的,但是如果在 P2 返回後呼叫 P1,則它將是懸空指標的錯誤使用。
對於物件,可以對任何型別使用 Unrestricted_Access。但是,如果結果是訪問不受約束的陣列子型別的型別,則結果指標與屬性的上下文具有相同的範圍,並且不得返回到某個封閉的範圍。例如,如果一個函式使用 Unrestricted_Access 建立一個訪問不受約束陣列的指標並將其返回值給呼叫者,則結果將涉及懸空指標。此外,僅當指標具有正常的預設“胖”表示形式時,才能使用此屬性建立指向不受約束陣列的指標,其中指標有兩個元件,一個指向陣列,另一個指向邊界。如果使用大小子句強制指向不受約束陣列的指標的“瘦”表示形式,其中只有一個指標的空間,則生成的指標不可用。
在 Unrestricted_Access 直接嘗試為非別名物件建立瘦指標的簡單情況下,編譯器會拒絕該使用,因為它是非法的,如下例所示
with System; use System;
procedure SliceUA2 is
type A is access all String;
for A'Size use Standard'Address_Size;
procedure P (Arg : A) is
begin
null;
end P;
X : String := "hello world!";
X2 : aliased String := "hello world!";
AV : A := X'Unrestricted_Access; -- ERROR
|
>>> illegal use of Unrestricted_Access attribute
>>> attempt to generate thin pointer to unaliased object
begin
P (X'Unrestricted_Access); -- ERROR
|
>>> illegal use of Unrestricted_Access attribute
>>> attempt to generate thin pointer to unaliased object
P (X(7 .. 12)'Unrestricted_Access); -- ERROR
|
>>> illegal use of Unrestricted_Access attribute
>>> attempt to generate thin pointer to unaliased object
P (X2'Unrestricted_Access); -- OK
end;
但其他情況無法被編譯器檢測到,並且被認為是錯誤的。考慮以下示例
with System; use System;
with System; use System;
procedure SliceUA is
type AF is access all String;
type A is access all String;
for A'Size use Standard'Address_Size;
procedure P (Arg : A) is
begin
if Arg'Length /= 6 then
raise Program_Error;
end if;
end P;
X : String := "hello world!";
Y : AF := X (7 .. 12)'Unrestricted_Access;
begin
P (A (Y));
end;
一個普通的不受約束的陣列值或一個標記為別名的約束陣列物件在記憶體中的邊界就在陣列之前,因此瘦指標可以同時檢索資料和邊界。但在這種情況下,非別名物件 X 在字串之前沒有邊界。如果型別 A 的大小子句不存在,則指標將是胖指標,其中一個元件是指向邊界的指標,一切都會正常。但是,如果存在大小子句,則在呼叫中從胖指標到瘦指標的轉換會丟失邊界,因此這是錯誤的,程式可能會引發 Program_Error 異常。
通常,建議完全避免混合使用瘦指標和 Unrestricted_Access,其中指定的型別是不受約束的陣列。瘦指標的使用應限於移植隱式假設指標大小的遺留程式碼的情況,並且此類程式碼在任何情況下都不應使用此屬性。
如果將屬性應用於常量,則會出現另一種錯誤情況。生成的指標可用於訪問常量,但嘗試以這種方式修改常量所產生的效果未定義。考慮此示例
P : constant Integer := 4;
type R is access all Integer;
RV : R := P'Unrestricted_Access;
..
RV.all := 3;
在這裡,我們嘗試將常量 P 從 4 修改為 3,但編譯器可能會或可能不會注意到此嘗試,並且後續對 P 的引用可能會產生值 3 或值 4,或者如果編譯器決定將 P 放入只讀記憶體中,則賦值可能會失敗。Unrestricted_Access 可以以這種方式使用的一個特殊情況是修改 in 引數的值
procedure K (S : in String) is
type R is access all Character;
RV : R := S (3)'Unrestricted_Access;
begin
RV.all := 'a';
end;
通常,這是一種冒險的方法。它可能看起來“有效”,但此類 Unrestricted_Access 的使用可能不可移植,即使是在 GNAT 的不同版本之間,因此最好避免使用。