When we do object oriented programming, some class instances represent things that are rather large.
There are two ways of looking at such a class: One is that it is or contains the representation of whatever it's representing. A string, for example. We call that "value semantics". If you make a copy of an object with value semantics, you get a copy of the whole thing: That's what copying "means" for that class; that's why we call it "semantics".
The other approach is "pointer semantics": The object you have in your hand is to be thought of as a reference to the data. If you make a copy of an object with pointer semantics, the "meaning" of that operation is like making a copy of a pointer: You get a new, and quite compact, reference to the same old thing.
The "pointer semantics" approach is common with string classes. Many instances of a pointer-semantical class can share the same data "inside". Naturally, you need to implement a reference count to make this work sanely in a non-garbage-collected language like C++. Among other things, this means that you can return a CString instance from a function without worrying about either unacceptable copying overhead or memory leaks.
The downside of pointer semantics is that it's counter-intuitive for most programmers. When you write a function like the following, you expect that you can change the str argument without disturbing the code that passed it in:
int foo( psstring str )
{
// blah blah blah
}
...but you just might be wrong. str will be a new instance of psstring, and if you assign to it, it'll probably (depending on implementation; who knows?) start pointing to something new and the caller's instance will be safe and sound. If, however, you lock str's buffer and modify it, you are generating side effects without the common courtesy of declaring the argument as a non-const reference or pointer. Let's face it, most operations on a string are inefficient, and we like to do what we can to improve matters by doing as many of them in-place as we can. Bang, bang! More ways to shoot yourself in the foot. The only way out is to for any operation that changes the string to check the reference count first, and duplicate the buffer if the count is greater than one. Once you do that, your semantics are no longer those of a pointer, are they? They're some bizarre mix between pointer and value.
On the whole, pointer semantics aren't such a swift idea. If you want a pointer, declare one.