Counting references on strings is thread-safe. Locks and exception handlers are used to safeguard the process. Consider the following code, with comments indicating where the compiler inserts code at compile time to manage reference counts:
procedure PassWithNoModifier(S: string); // prologue: Increase reference count of S (if non-negative), // and enter a try-finally block begin // Create a new string to hold the contents of S and 'X'. Assign the new string to S, // thereby reducing the reference count of the string S originally pointed to and // brining the reference count of the new string to 1. // The string that S originally referred to is not modified. S := S + 'X'; end; // epilogue: Enter the `finally` section and decrease the reference count of S, which is // now the new string. That count will be zero, so the new string will be freed. procedure PassWithConst(const S: string); var TempStr: string; // prologue: Clear TempStr and enter a try-finally block. No modification of the reference // count of string referred to by S. begin // Compile-time error: S is const. S := S + 'X'; // Create a new string to hold the contents of S and 'X'. TempStr gets a reference count // of 1, and reference count of S remains unchanged. TempStr := S + 'X'; end; // epilogue: Enter the `finally` section and decrease the reference count of TempStr, // freeing TempStr because its reference count will be zero.
As shown above, introducing temporary local string to hold the modifications to a parameter involves the same overhead as making modifications directly to that parameter. Declaring a string
const only avoids reference counting when the string parameter is truly read-only. However, to avoid leaking implementation details outside a function, it is advisable to always use one of
out on string parameter.