Provided by: manpages-zh_1.5.2-1.1_all bug

NAME

       Tcl_NewObj,    Tcl_DuplicateObj,    Tcl_IncrRefCount,    Tcl_DecrRefCount,   Tcl_IsShared,
       Tcl_InvalidateStringRep - 操縱 Tcl 對象

總覽 SYNOPSIS

       #include <tcl.h>

       Tcl_Obj *
       Tcl_NewObj()

       Tcl_Obj *
       Tcl_DuplicateObj(objPtr)

       Tcl_IncrRefCount(objPtr)

       Tcl_DecrRefCount(objPtr)

       int
       Tcl_IsShared(objPtr)

       Tcl_InvalidateStringRep(objPtr)

參數 ARGUMENTS

       Tcl_Obj   *objPtr   (in)      指向一個對象;必須是以前調用 Tcl_NewObj 返回的結果。
_________________________________________________________________

介紹 INTRODUCTION

       這個手冊頁提供了對 Tcl 對象以及如何使用它們的一個概述。它還描述了管理 Tcl 對象的一些一般過
       程。使用這些過程來建立和複製對象,和增加和減少到對象的引用(指針)計數。這些過程與那些在特定
       類型的對象如 Tcl_GetIntFromObjTcl_ListObjAppendElement  上進行操作的過程聯合使用。單獨
       的過程和它們所操縱的數據結構被放在一起描述。

       Tcl 的雙端口(dual-ported)對象為存儲和交換 Tcl 值提供了一個通用的機制。它們在很大程度上替代
       了 Tcl 中字符串的使用。例如,它們被用來存儲變量值、命令參數、命令結果、和稿本。Tcl  對象外
       在表現很像字符串,但它還持有可以被更加有效的操縱的內部表示。例如,現在一個  Tcl 列表被表示
       為持有列表的字符串表示的一個對象,如同到每個列表元素的指針的一個數組。雙端口對象避免了運行
       時的類型轉換。它們還提高了許多操作的速度,原因是可以立即獲得一個適當的表示。編譯器自身使用
       Tcl 對象來緩存(cache)作為編譯稿本的結果的字節碼指令。

       這兩種表示互為緩存並且被以懶惰方式計算。就是說,每個表示都只在需要時才被計算,它被從另一種
       表示計算出來,而一旦被計算出來了,它就被保存起來。除此之外,其中一個表示的改變將使另一個表
       示成為無效 。舉個例子,一個做整數運算的 Tcl 程式可以在一個變量的內部機器整數表示上進行直接
       操作,而不需要經常性的在整數和字符串之間進行轉換。只有在需要這個變量的值的一個字符串表示的
       時候,比如列印它,程式才重新生成這個整數的字符串表示。儘管對象包含一個內部表示,但它們的語
       義仍是依據字符串定義的: 總是可以獲取最新的字符串,在取回對象的字符串表示的時候,對對象的任
       何改變都將反映到取回的那個字符串上。因為這個表示是無效的並被重新生成了,擴展作者直接訪問
       Tcl_Obj  的字段是很危險的。最好使用 Tcl_GetStringFromObjTcl_GetString 這樣的過程來訪問
       Tcl_Obj 信息。

       在堆上分配對象,使用到它們的 Tcl_Obj  結構的指針引用對象。對象要盡可能的共享。這將顯著的縮
       減存儲需求,原因是一些對象比如長列表是非常大的。還有,多數  Tcl 值只是被讀而從不被修改。尤
       其是過程參數,它們可以在調用和被調用的過程之間共享。賦值和參數綁定是通過簡單的賦予到這個值
       的一個指針完成的。使用引用計數來確定什麼時候歸還一個對象的存儲是安全的。

       Tcl 對象是有類型的(typed)。一個對象的內部表示由它自己的類型來控制。在 Tcl 核心中預定義了七
       種類型,其中包括:整數、雙精度浮點數、列表、和字節碼。擴展作者可是使用 Tcl_RegisterObjType
       過程來擴展類型的集合。

對象結構 THE TCL_OBJ STRUCTURE

       每個 Tcl 對象都被表示為一個 Tcl_Obj 結構,其定義如下。
              typedef struct Tcl_Obj {
                int refCount;
                char *bytes;
                int length;
                Tcl_ObjType *typePtr;
                union {
                   long longValue;
                   double doubleValue;
                   VOID *otherValuePtr;
                   struct {
                     VOID *ptr1;
                     VOID *ptr2;
                   } twoPtrValue;
                } internalRep;
              } Tcl_Obj;
       byteslength 成員一起持有一個對象的字符串表示,這是一個已計數的 (counted) 字符串或二進
       制串 (binary string),二進制串可能包含有嵌入的 null 字節的二進制串。bytes 指向這個字符串表
       示的第一個字節。length  成員給出字節數。字節數組的在偏移量 length 上,也就是最後一個字節後
       面必須總是有一個 null;這允許不包含 null 的字符串表示被作為一個常規的用 null 終結的 C 語言
       字符串來對待。  C  程式使用 Tcl_GetStringFromObjTcl_GetString 來得到一個對象的字符串表
       示。如果 bytes 是 NULL,則字符串表示無效。

       一個對象的類型管理它的內部表示。成員 typePtr 指向描述類型的 Tcl_ObjType 結構。如果 typePtr
       is 是 NULL,則內部表示無效。

       internalRep 聯合成員持有一個對象的內部表示。它可以是一個(長)整數,一個雙精度浮點數,或者一
       個指針、它指向包含這個類型的對象要表示對象所需要的補充信息的值,或者是兩個任意的指針。

       使用 refCount 成員來通告在什麼時候釋放一個對象的存儲是安全的。它持有到這個對象的活躍引用的
       計數。維護正確的引用計數是擴展作者的一個關鍵性的責任。在下面的對象的存儲管理      (STORAGE
       MANAGEMENT OF OBJECTS) 章節中討論了引用計數。

       儘管擴展的作者可以直接訪問一個 Tcl_Obj  結構的成員,但最好還是使用恰當的過程和宏。例如,擴
       展作者永遠不要直接讀或修改    refCount;作為替代,他們應當使用像    Tcl_IncrRefCountTcl_IsShared 這樣的宏。

       Tcl 對象的一個關鍵屬性是它持有兩個表示。典型的,一個對象開始時只包含一個字符串表示: 它是無
       類型的並且typePtr  是一個 NULL。分別使用 Tcl_NewObjTcl_NewStringObj 建立包含一個空串的
       一個對象或一個指定字符串的一個復件。一個對象的字符串值可以使用   Tcl_GetStringFromObjTcl_GetString   來獲取並使用   Tcl_SetStringObj   來改變它。如果如果這個對象以後被傳遞給像
       Tcl_GetIntFromObj 這樣的要求一個特定的內部表示的過程,則這個過程將建立一個內部表示並設置這
       個對象的 typePtr。從字符串表示來計算它的內部表示。一個對象的兩個表示是雙重的: 對一個的改變
       也將反映到另一個上。例如,Tcl_ListObjReplace            將修改一個對象的內部表示,下一個到
       Tcl_GetStringFromObjTcl_GetString 的調用將反映這個改變。

       出於效率的原因以懶惰方式重計算表示。一個過程如 Tcl_ListObjReplace 對一個表示的改變不立即反
       映到另一個表示上。作為替代,把另一個表示標記為無效,如果以後需要的話再重新生成。多數 C  程
       式員永遠無須關心這是如何完成的,他們只是簡單的使用像        Tcl_GetBooleanFromObjTcl_ListObjIndex  這樣的過程。而實現自己的對象類型的程式員必須檢查無效表示和在需要時標記一
       個表示為無效。使用過程  Tcl_InvalidateStringRep 來標記一個對象的字符串表示為無效並釋放與這
       個字符串表示相關聯的存儲。

       對象在它的一生當中通常保持一種類型,但是有時一個對象必須從一種類型轉換成另一種類型。例
       如,一個  C  程式可以通過重復調用 Tcl_AppendToObj 來在一個對象中建造一個字符串,並接著調用
       Tcl_ListObjIndex  來從一個對象中提取一個列表元素。持有相同字符串的同樣的對象在不同的時候可
       能有多種不同的內部表示。擴展作者可以使用  Tcl_ConvertToType 過程強制把一個對象從一種類型轉
       換成另一種類型。只有建立新對象類型的程式員才需要關心這是如何作的。作為對象類型實現的一部
       分,需要定義為一個對象建立一個新的內部表示和改變它  typePtr 的一個過程。如何建立一個新對象
       類型請參見 Tcl_RegisterObjType 手冊頁。

對象生命周期示例 EXAMPLE OF THE LIFETIME OF AN OBJECT

       作為一個對象生命周期的一個例子,考慮下列命令序列:
              set x 123
       這裏把一個未知類型的對象賦值給 x,這個對象的 bytes 成員指向 123length 成員包含 3。對象
       的 typePtr 成員是 NULL。
              puts "x is $x"
       x 的字符表示是有效的(因為 bytes 是非 NULL)並被這個命令取回。
              incr x
       incr  命令首先通過調用 Tcl_GetIntFromObj 從 x (所引用的)的對象的得到一個整數。這個過程檢查
       這個對象是否已經是一個整數對象。由於它不是,就通過把這個對象的 internalRep.longValue  成員
       設置為整數 123,並把這個對象的 typePtr 設置為指向整數的 Tcl_ObjType 結構,此過程把這個對象
       轉換成了整數對象。兩個表示現在都是有效的。incr  增加這個對象的整數內部表示,接著使它的字符
       串表示無效(通過調用 Tcl_InvalidateStringRep),原因是這個字符串表示不再與內部表示相對應了。
              puts "x is now $x"
       現在需要  x (所引用的)的對象的字符串表示,要重新計算它。字符串表示現在是 124。兩個表示又都
       是有效的了。

對象的存儲管理 STORAGE MANAGEMENT OF OBJECTS

       Tcl 對象在堆上分配,並且要盡可能的共享對象來縮減存儲需求。使用引用計數來確定何時一個對象不
       再被需要並可以被安全的釋放。剛用  Tcl_NewObjTcl_NewStringObj 建立的對象的  refCount 是
       0。當建立到這個對象的一個新引用時,使用宏 Tcl_IncrRefCount  增加引用計數。當不再需要一個引
       用的時候  ,使用 Tcl_DecrRefCount 減少引用計數,而且如果這個對象的引用計數下降到零,就釋放
       它的存儲。被不同的代碼或數據結構共享的一個對象的 refCount 大於 1。增加一個對象的引用計數來
       確保它不會被過早釋放或者它的值被意外的改變。

       舉個例子,字節碼解釋器在調用者和被調用的過程之間共享參數對象,以避免複製對象。它把調用者的
       實際參數的對象賦值給過程的形式參數變量。此時,它調用   Tcl_IncrRefCount   來增加每個實際參
       數(所引用的)的對象的引用計數,原因是有了從形式參數到這個對象的一個新引用。在被調用的過程返
       回的時候,解釋器調用 Tcl_DecrRefCount 來減少每個參數的引用計數。當一個對象的引用下降到小於
       等於零的時候, Tcl_DecrRefCount 歸還它的存儲。多數命令過程不是必須關心引用計數的,原因是它
       們立即使用一個對象的值並且在它們返回之後不保留到這個對象的指針。但是,如果它們把到一個對象
       的指針保留到一個數據結構中,則他們必須注意要增加它的引用計數,原因是這個保留的指針是一個新
       引用。

       像 lappendlinsert  這樣的直接修改對象的命令過程必須注意要在修改一個共享的對象之前複製
       它。  他們必須首先調用 Tcl_IsShared 來檢查這個對象是否是共享的。如果對象是共享的,則他們必
       須使用  Tcl_DuplicateObj 複製這個對象;它返回原始對象的一個新複製品,其 refCount 是  0。如
       果對象未被共享,則命令過程「擁有」這個對象並可以安全的直接修改它。例如,下列代碼出現在實現
       linsert 的命令過程當中。通過在  index 的前面插入 objc-3 新元素,這個過程修改在 objv[1]  中
       傳遞給它的列表對象 。

              listPtr = objv[1];
              if (Tcl_IsShared(listPtr)) {
                listPtr = Tcl_DuplicateObj(listPtr);
              }
              result = Tcl_ListObjReplace(interp, listPtr, index, 0, (objc-3), &(objv[3]));

       另一個例子,incr    的命令過程在增加變量(所引用的)對象內部表示中的整數之前,必須檢查這個變
       量(所引用的)對象是否是共享的。如果它是共享的,則需要複製這個對象,目的是避免意外的改變在其
       他數據結構中值。

參見 SEE ALSO

       Tcl_ConvertToType,    Tcl_GetIntFromObj,    Tcl_ListObjAppendElement,    Tcl_ListObjIndex,
       Tcl_ListObjReplace, Tcl_RegisterObjType

關鍵字 KEYWORDS

       internal representation, object, object creation, object type, reference counting,  string
       representation, type conversion

[中文版維護人]

       寒蟬退士

[中文版最新更新]

       2001/10/30

《中國 Linux 論壇 man 手冊頁翻譯計劃》:

       http://cmpp.linuxforum.net