Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a std::string without copying?

I have a class say,

class Foo
{
   public:
      void ProcessString(std::string &buffer)
      {
          // perform operations on std::string

          // call other functions within class
          // which use same std::string string
      }

      void Bar(std::string &buffer)
      {
          // perform other operations on "std::string" buffer
      }

      void Baz(std::string &buffer)
      {
          // perform other operations on "std::string" buffer
      }
};

This class tries to use a std::string buffer to perform operations on it using various methods under these conditions:

  • I don't want to pass a copy of std::string which I already have.
  • I don't want to create multiple objects of this class.

For example:

// Once an object is created
Foo myObject;

// We could pass many different std::string's to same method without copying
std::string s1, s2, s3;
myObject.ProcessString(s1);
myObject.ProcessString(s2);
myObject.ProcessString(s3);

I could use the string and assign it as a class member so that other functions using can know about it.

But it seems we cannot have a reference class member std::string &buffer because it can only be initialized from constructor.

I could use a pointer to std::string i.e. std::string *buffer and use it as a class member and then pass the addresses of s1, s2, s3.

class Foo
{
   public:
      void ProcessString(std::string *buf)
      {
          // Save pointer
          buffer = buf;

          // perform operations on std::string

          // call other functions within class
          // which use same std::string string
      }

      void Bar()
      {
          // perform other operations on "std::string" buffer
      }

      void Baz()
      {
          // perform other operations on "std::string" buffer
      }
   private:
       std::string *buffer;
};

Or, the other way could be pass each functions a reference to std::string buffer just as shown in the first example above.

Both ways kind of seem a bit ugly workarounds to be able to use a std::string without copying as I have rarely seen the usage of std::string as a pointer or pass all the functions of class the same argument.

Is there a better around this or what I'm doing is just fine?

like image 981
cpx Avatar asked Aug 23 '14 16:08

cpx


People also ask

Does std::string make a copy?

std::string object will allocate internal buffer and will copy the string pointed to by ps there. Changes to that string will not be reflected to the ps buffer, and vice versa. It's called "deep copy".

Is std::string the same as string?

There is no functionality difference between string and std::string because they're the same type. That said, there are times where you would prefer std::string over string .

Should I use std::string?

Use std::string when you need to store a value. Use const char * when you want maximum flexibility, as almost everything can be easily converted to or from one. Save this answer.

Are C strings faster than std::string?

C-strings are usually faster, because they do not call malloc/new. But there are cases where std::string is faster. Function strlen() is O(N), but std::string::size() is O(1). Also when you search for substring, in C strings you need to check for '\0' on every cycle, in std::string - you don't.


1 Answers

Keeping in MyObject a reference or a pointer to a string which is not ownned by your object is dangerous. It will be easy to get nasty undefined behaviour.

Look at the following legal example (Bar is public):

myObject.ProcessString(s1);     // start with s1 and keep its address
myObject.Bar();                 // works with s1 (using address previously stored) 

Look at the following UB:

if (is_today) {
    myObject.ProcessString(string("Hello"));  // uses an automatic temporary string
}                                             // !! end of block: temporary is destroyed!
else {
    string tmp = to_string(1234);            // create a block variable 
    myObject.ProcessString(tmp);             // call the main function 
}                                            // !! end of block:  tmp is destroyed
myObject.Bar();  // expects to work with pointer, but in reality use an object that was already destroyed !!  => UB                              

The errors are very nasty, because when reading function's usage, everything seems ok and well managed. The problem is hidden by automatic destruction of bloc variables.

So if you really want to avoid the copy of the string, you could use a pointer as you envisaged, but you shall only use this pointer in functions called directly by ProcessString(), and make these functions private.

In all other case, I'd strongly suggest to reconsider your position, and envisage:

  • a local copy of the string in the object that shall use it.
  • Or use a string& parameters in all the object's function that need it. This avoids the copies but leaves to caller the responsibility of organising the proper management of the string.
like image 131
Christophe Avatar answered Oct 03 '22 08:10

Christophe