Any time you modify a string, mostly when adding to it, it has to reallocate the memory for that string. This usually involves a new memory allocation, copy the contents of the old string to the new buffer, then free the old memory buffer. Look at this over the lifetime of an application and you are talking about potentially millions of tiny memory allocations and frees.

If you ever declare variable as a string or a dynamic array in a function, the compiler secretly adds a try..finally block around the entire function to free the memory used by those strings and dynamic arrays. That takes extra CPU time to execute each time the function is called.

TObject, the base class for every class, contains string members. The process to create an instance of a TObject-derived class is quite expensive. The creation of each instance of a TObject-derived class involves multiple calls to InstanceSize(), which then allocates the memory for that instance. It then sets that memory to zero. It then performs a while loop, inside which is a for loop, to setup the interface table for that class instance. Remember, this is for each instance of the class that you create. There are similar expenses when destroying the class instance.

Unless you override some base methods in TObject, there is also no way to bulk-allocate a bunch of class instances. Each class instance must be created individually. That is horribly expensive when you might be talking about using a class for a vector or matrix. A mesh might have 10,000 vertices. That is a lot of needless overhead, and the time wasted can be noticed by the end user in longer load times.

As I said, this comes from having a commercial games development, and the amount of overhead in using TObject and strings rings alarm bells in my head.