Avoid unnecessary dynamic allocations
Right now, you're dynamically allocating memory, and copying the input data into that dynamically allocated buffer, almost exclusively so you can use strtok
to do the tokenizing.
Avoid unnecessary work
You're also zeroing that entire block of dynamically allocated memory, then immediately copying the data from the input over the zeros you just wrote into the block.
If you're going to mess with something like this at all, you can probably speed it up at least a little just by avoiding that unnecessary zeroing.
auto *s = new char [strlen(input)+1];
strcpy(s, input);
Get from input to output as directly as possible
I'd rather avoid that amount of copying, and create results directly from the input data instead, something like this:
std::vector<std::string> tokenize(std::string const &in) {
char sep = ' ';
std::string::size_type b = 0;
std::vector<std::string> result;
while ((b = in.find_first_not_of(sep, b)) != std::string::npos) {
auto e = in.find_first_of(sep, b);
result.push_back(in.substr(b, e-b));
b = e;
}
return result;
}
Although it's difficult to be certain without profiling, my guess is that this has less overhead than using a stringstream or a regex token iterator to do the job. A heavily optimized regex library might get pretty close (possibly even close enough to make differences irrelevant). stringstreams can make for neat code, so as long as you don't care about the overhead, they're often an excellent choice--but at a guess, using a stringstream is going to end up slower than your current code.
Consider using string_view
instead of string
If you can afford to restrict your code to C++17 (or later), consider using a string_view
for the input, rather than an std::string
. A string_view gives you string-like access to some data, but without actually storing that data in the string_view object itself. At least in the typical implementation, it stores only the location and length of the data.
The same principle can be applied to the result: instead of each result being a string
that holds a copy of the data from the input, you might be able to use a string_view
that holds only a location and length for each. This can be a little tricky though. In particular, it makes you responsible for lifetime issues--since you're storing references to the original input data, you need to ensure that the original input data remains valid for as long as you might use the tokens you've created from that input. In your case (string literals) that's pretty trivial (they have static storage duration, so they're always valid). For real use, where you're tokenizing some string that came from user input, it may be more difficult (but if you can do it, it can be a substantial win).
Be sure you're using a current standard library
Many older implementations of the standard library used implementations of std::string
that always allocated storage for the string's data dynamically. This typically means creating a string has a fairly substantial amount of overhead, regardless of its size.
More recent implementations of the standard library typically use what's called a "short string optimization". This means they allocate enough storage for a short string (for some arbitrary definition of short, but usually around 16-32 characters) inside the string object itself. This avoids a dynamic allocation for the buffer space when you're storing lots of small strings (e.g., your result tokens/words, probably). The speed difference can be pretty dramatic (I've seen around 10:1 pretty routinely).
strtok()
is not a good choice, but please remember to Be Nice! \$\endgroup\$strtok(...)
is bad, what should I use? Sorry if this is offensive code, but it is kind of the reason why I am asking for a review. \$\endgroup\$strtok
is considered to be 'bad' and instead just supplied an example of a good C++ way to split strings, I'll attempt to explain whystrtok
is considered to be 'bad'. Firstly the factstrtok
modifies its first argument is somewhat frowned upon (as are most functions that modify their arguments). Secondly it can't be used onconst char*
s. And lastly (and most importantly) it's not thread safe. Ifstrtok
is used on more than one thread, things will break. \$\endgroup\$