When should std::string be used over character arrays?
Asked Answered
S

7

34

I sit around and think about whether or not I should be using const char* or const std::string& pretty often when I'm designing class interfaces and when it comes down to it, I often feel like there's 6 in one hand with half a dozen in the other.

Take the following two function prototypes:

void foo(const char* str);
void foo(std::string str);

If the foo function were to store a string, I would say the second is a better option due to the ability to pass a string and utilize move semantics where possible. However, if foo only needed to read the string, would the const char* solution be better?

On a performance perspective, a temporary std::string wouldn't need to be created. However, calling the function with an already existing string as an argument looks obtrusive: foo(mystr.c_str()). Worse yet, if more advanced operations need to be done on the array at some point down the road or if a copy should be stored, the interface then has to change.

So my questions is this:

Are there well defined, either personal or otherwise, conventions that rule when std::string or const char* is a better choice? Furthermore, when starting a new project, is it best to be consistent with usage or just take whichever one fits the current chunk of code best?

Sapienza answered 30/4, 2014 at 8:38 Comment(9)
One simple rule is this: if you are unsure, then you'd better use std::string.Iden
if you don't want to create a temporary string, use void foo(const std::string &bar)Wellbred
@RedAlert wouldn't the call foo("hello") cause a temporary to be created using the std::string variant?Sapienza
string is designed for strings, const char * is only suitable for constant string literals. For string variables, using char * is a no-no. Speed is not going to matter 99.9% of the times, and if your string has 15 characters they must be stored somewhere anyway, there is no magic that will let you save that storing space. Pass by reference does not duplicate the data and if you're passing your parameters by value all the time, you're doing it very bad.Younker
I'd always prefer std::string. It saves you so much hassle later on. Trust me.Overnight
I would opt for a string_ref-like class over either.Orchestral
Related: #8015855 (in fact, my answer there half-applies here too).Derosier
If development time and reliability are more important than performance and compactness. Which is to say, 99% of the time. Exceptions are things like inner loops of game engines, embedded programs that need to be small, kernel code where the standard library is unavailable...Zoa
Other reason to use std::string is that it can store '\0'.Barghest
K
53

const char* is a throwback to C. I'd say that in decent C++, about the only use for it is in extern "C" APIs.

std::string has a number of advantages:

  1. It provides a constant-time size() function. Discovering the length of a const char* takes linear time.

  2. It is guaranteed to be valid. A const char* has to be checked for being null, and it is entirely possible to pass incorrect data - data missing a null terminator. Such an occurrence is pretty much guaranteed to result in a crash or worse.

  3. It is compatible with standard algorithms.

If you're worried about performance impacts of having to create a std::string to call the function, consider taking the approach the standard library uses - change the function to take a pair of iterators instead. You can then provide a convenience overload taking a const std::string& delegating to the iterator-pair one.

Kathrynekathy answered 30/4, 2014 at 8:47 Comment(10)
I'm much less worried about performance than I am about the coherence of the interface as a whole. I find that code that's easier to read is also easier to debug and easier to optimize.Sapienza
string is better here as well. Compare myFunc(const char*) and myFunc(string).Unstable
@Unstable you mean myFunc(std::string).Halonna
No. using std::string in headers is a must.Unstable
@Unstable No. Typing using anyNamespaceOrType in a header is a very bad idea, for reasons I hope I don't have to explain. It's misleading to recomend this, especially with unilateral words like "must". In fact, many people, including me, don't even bother using std anywhere.Neurophysiology
'Misleading' in this case is using overly broad statements. Please explain why using std::string is a bad idea (not using namespace std, but exactly std::string)Unstable
@Unstable Familiarity and clarity. That's the primary reason. If I look into code and see std::string, it's immediately obvious what it is. If I see string, I have to wonder: is there a using somewhere? Or is it a typedef or a custom-provided class which perhaps imitates std::string, perhaps not?Kathrynekathy
This is entirely subjective and does not constitute a "best practice" or something. I don't claim my position is absolute objective truth either, but then I'm not trying to present it as one.Unstable
@Unstable "using std::string in headers is a must" looks quite strong to me.Kathrynekathy
Well, I would have apologized for the form of my message. But the dogmatic & patronizing tone in the comment above ("bad idea for reasons I hope I don't have to explain") doesn't leave me with desire to, so - I'll leave it at that.Unstable
H
21

Just a few notes from personal experience. If you are working on a c++ project (as per the tag you added) stick to the std::string provided as much as you can. Do not try to reinvent the most basic of all structures, the almighty string. I saw several projects where they reinvented the basic string and afterwards spent months fine tuning it.

In case of your c++ project from the moment you have introduced a char* variable you fall back to standard C functions, such as strlen() and strcpy (just to name two ...). And from this point your project starts turning in a mess, with hand managed memory allocation, etc...

In case you need to interact with third party libraries which accept const char* as their parameter (and I suppose you trust those libraries - ie: you trust they do not const_cast away the constness to do evil things with your poor string) you can use std::string::c_str() to get the const char* out of your string.

In case you need to interact with libraries which have methods accepting char* I highly recommend you take a copy of your string's c_str() and use that as the ingoing parameter to the library (of course, do not forget to delete the extra copy).

Beside of these extra points I just subscribe to all three points from Angew's response.

Hypnosis answered 30/4, 2014 at 8:57 Comment(2)
Thanks for the advice, it was genuinely helpful.Sapienza
This would have been my answer too, especially the "if you find yourself in need of the C functions, just use std::string already" bit.Coffee
C
6

std::string should always be the first choice in c++. To avoid extra copy overhead you should almost always pass argument as reference and const reference whereever and whenever possible.

In that case your function signature would become like this

void foo(std::string &str);

and like this if argument is const

void foo(const std::string &str);

There are benifits of this const key word that you can look here

Candlepower answered 30/4, 2014 at 8:45 Comment(3)
No, in C++11 if you need to copy the string anyway you should pass by value rather than const reference.Effloresce
Also, sometimes its not best to pass by reference for builtin, See #5347353Excrescent
Yes it is Its only where and when possible.. as it improves API readibilityCandlepower
S
4

std::string is better than using raw char * where you have to manage allocations, deallocations and size related issue to be careful for any overflow, underflow, that you are not crossing the size boundary. Whereas std::string these are all taken care of through abstraction

Savick answered 30/4, 2014 at 8:45 Comment(0)
H
4

Instead of using

void foo(std::string str); 

you could use

void foo(const std::string& str);

which would be equivalent to

void foo(const char* str);

in terms of usage, because no memory is allocated when passing a reference. But for strings in C++ I'd definitely use std::string. For random data or C compatible interfaces I wouldn't.

Harwood answered 30/4, 2014 at 8:45 Comment(7)
const std::string& may have to new data if a string literal is passed( needs to be created ). const char* may avoid this allocation at run time.Bravissimo
The only problem I ever have with using const std::string& is when I know I'll need a copy to be made. That's the rationale for choosing std::stringSapienza
@Bravissimo a char[] is also created when calling a function with a string literal. Nothing you gain from using char*Harwood
@Sapienza if you need a copy you can still construct a fresh string by calling std::string(str) with str beeing const std::string& strHarwood
@Harwood Doing that would destroy the benefits of move semantics in c++11.Sapienza
@Harwood basically, if you know you're going to copy it, you should use std::string. Call like foo(some_func_that_returns_a_string()) would pass their result via move semantics directly into the next call. In a lot of cases, even the move can be elided. See https://mcmap.net/q/15702/-what-is-move-semanticsSapienza
@Theolodis: Calling your third foo as foo("Bar") will not cause dynamic allocation, while calling the second one might (if no SSO is applied). The two certainly aren't equivalent.Prefrontal
A
3

If you want better handling of your say data (in your case it will be string) i'd say go with char *. It will give you better access to your string and memory utilization. But if you don't have to worry about performance and memory management issue than you can use std::string easily.

Aureolin answered 30/4, 2014 at 14:31 Comment(2)
That's a difficult position for me to endorse because most of the time, performance and memory management aren't important enough to warrant confusing code or potential programming bugs.Sapienza
Agree... but its upto coder how he/she writes the code [confusing or easily understandable]. Although if we look for bigger picture than memory utilization is more important [e.g. transferring data on n/w]Aureolin
I
1

Well, I would rephrase your question slightly. You should ask when character array should be used instead of std::string.

It is because you should always use string unless you cannot use it. So situations where you should use characters array instead of string:

  1. Using APIs that supports C so you cannot use string.
  2. Passing data between exe and dll. It is not recommended to exchange data types that have constructor and/or destructor between exe and dll

If you worry about performance, after compilation with all optimizations enabled string becomes similar to character array, maybe gives even better performance in some cases, for example if you need to get number of characters it contains.

Also about performance, you can always pass by reference, as other answers mentioned, in case you have to pass a pointer to character array, you can use method c_str() which returns const pointer to first character, if you have to pass pointer (not const pointer) you can (but actually shouldn't) pass it like this: &str[0].

Imbrue answered 6/5, 2014 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.