What is the difference between the following declarations? - c++

string str("Hello World");
string str="Hello World";
I don't seem to understand the difference between the two. According to my textbook, the operation that the first statement performs is "Initialization constructor using C string". So does the first statement define a C string and the second statement define a C++ string? Also please explain the difference between a C string and a C++ string.

Both lines create a C++ std::string named str. And both initialize them from a C string. The difference is, how they are initialized:
The first is direct initialization:
string str("Hello World");
This calls the string(const char *) constructor.
The second is copy initialization:
string str = "Hello World";
This needs that the string(const char *) constructor is non-explicit (for the previous method, the constructor can be explicit).
We have a little bit differing behavior, depending on the version of the standard:
pre C++17: first, a temporary object is created (with string(const char *)), and then the copy (or move) constructor is called to initialize str. So, the copy (or move) constructor needs to be available. The copy constructor phase can be elided (so the object will be created just like as the direct initialization case), but still, the copy (or move) constructor needs to be available. If it is not available, the code will not compile.
post C++17: here, because the standard guarantees copy elision, only the string(const char *) constructor is called. Copy (or move) constructor doesn't need to be available. If copy constructor is not available, the code still compiles.
So, for this particular case, there is no real difference in the end between the two initializations, and therefore, both str strings will become the same.

Both lines define a variable of type std::string named str that is constructed by the constructor of std::string that takes a char const* as its argument. There is no difference in those lines.
[...] C string [...] C++ string [...]?
What is commonly called a C-string is nothing but a zero terminated array of char:
"foobar"; // an array of 7 const chars. 7 instead of 6 because it is 0-terminated.
char foo[] = "foobar"; // an array of 7 chars initialized by the string given
std::string however is a class of the C++ standard library that manages string resources of dynamic length.

"Hello World" is the c-string (null terminated sequence of characters). string (or std::string as its complete name is) is a c++ string (not null terminated) in both cases.
Both lines call the same constructor that takes the c string and constructs a std::string.

Related

Function call parameter, char * vs string default constructor

While calling a function/method in C++11 and above, which one is better (if any difference)?
Lets assume this function/method:
void func(std::string s) { ... }
Which one is best between the following?
func(std::string())
or
func("")
And more generally, is there any advantage to always call the constructor explicitly during initialization or parameter passing?
It's better to call the default constructor, because it's guaranteed to not do any unnecessary work.
When passing an empty string literal, it could be that the string implementation does some work processing that string (compute its length for example). An empty string literal isn't a magic bullet that can be treated differently from non-empty string literals. It's type is const char[1], which decays into const char*, and that's it - the std::string constructor dealing with this literal will end up doing more work than necessary.
From cppreference for std::string::string():
Default constructor. Constructs empty string (zero size and unspecified capacity). If no allocator is supplied, allocator is obtained from a default-constructed instance.
... and for std::string::string(const char*):
Constructs the string with the contents initialized with a copy of the null-terminated character string pointed to by s. The length of the string is determined by the first null character. [...]
For further reading, see also this short article.
I would like to compare func(std::string()) with func(""):
func(std::string())
You create an std::string object with default parameter is empty string
Then pass std::string object to func function. You pass it by value, and a new std::string object will be allocated in stack memory, and call a copy constructor to initialized it.
In this case, there are two std::string object is allocated.
func("")
You pass an empty string, so compiler will allocate a std::string object in stack memory, and use std::string(const char*) constructor.
In this case, there is only 1 std::string object allocated.
So, I think for this specific case, func("") maybe better.

Which std::string constructor is being called here?

I am trying to construct a string in C++ as below.
const char *cstring = "abcd";
const string cppstr = string(cstring, cstring + strlen(cstring));
This is working fine and cppstr has the value "abcd" even though it doesn't match with any of the string constructors specified in the standard. Can any one please let me know which constructor of string is invoked in this particular case.
There is a templated constructor that takes as its input two InputIterators. (See the cppreference.org reference, constructor (6)). Raw C++ pointers meet all the requirements of InputIterators (in fact, they're RandomAccessIterators). Therefore, calling
string(cstring, cstring + strlen(cstring)
invokes this constructor. That constructor works by iterating across the range of the elements delineated by the iterator and constructing a string as a copy of those elements.
As a note, you can also just write
const string cppstr{cstring, cstring + strlen(cstring)};
here instead of assigning cppstr a value.

C++ string declaration

I am learning C++ from the beginning and I don't get the whole strings topic.
What is the difference between the following three codes?
std::string s = std::string("foo");
std::string s = new std::string("foo");
std::string s = "foo";
std::string s = std::string("foo");
This creates a temporary std::string object containing "foo", then assigns it to s. (Note that compilers may elide the temporary. The temporary elison in this case is explicitly allowed by the C++ standard.)
std::string s = new std::string("foo");
This is a compiler error. The expression new std::string("foo") creates an std::string on the free store and returns a pointer to an std::string. It then attempts to assign the returned pointer of type std::string* to s of type std::string. The design of the std::string class prevents that from happening, so the compile fails.
C++ is not Java. This is not how objects are typically created, because if you forget to delete the returned std::string object you will leak memory. One of the main benefits of using std::string is that it manages the underlying string buffer for you automatically, so new-ing it kind of defeats that purpose.
std::string s = "foo";
This is essentially the same as #1. It technically initializes a new temporary string which will contain "foo", then assigns it to s. Again, compilers will typically elide the temporary (and in fact pretty much all non-stupid compilers nowadays do in fact eliminate the temporary), so in practice it simply constructs a new object called s in place.
Specifically it invokes a converting constructor in std::string that accepts a const char* argument. In the above code, the converting constructor is required to be non-explicit, otherwise it's a compiler error. The converting constructor is in fact non-explicit for std::strings, so the above does compile.
This is how std::strings are typically initialized. When s goes out of scope, the s object will be destroyed along with the underlying string buffer. Note that the following has the same effect (and is another typical way std::strings are initialized), in the sense that it also produces an object called s containing "foo".
std::string s("foo");
However, there's a subtle difference between std::string s = "foo"; and std::string s("foo");, one of them being that the converting constructor can be either explicit or non-explicit in the above case.
std::string s = std::string("foo");
This is called copy initialization. It is functionally the same as direct initialization
std::string s( "foo" );
but the former does require that the copy constructor is available and compilers may create a temporary object but most will elide the temporary and directly construct s to contain "foo".
std::string s = new std::string("foo");
This will not compile because new returns a pointer. To make it work you'd need the type of s to be a std::string *. Then the line dynamically allocates an std::string object and stores the pointer in s. You'll need to delete it once you're done using it.
std::string s = "foo";
This is almost the same as first. It is copy initialization but it has an added constraint. It requires that the std::string class contains a non-explicit constructor that takes a const char *. This allows the compiler to implicitly construct a temporary std::string object. After that the semantics are identical to case 1.
Creates a temporary string object and copies the value to s
Does not compile, new std::string("foo") returns a pointer to some newly allocated memory.
For this to work, you should declare s as a pointer to a string std::string* s.
Constructs a string from a C-string.
You should use the third option in most - if not all - cases.
1 will create a temporary variable (right hand side), then call the assignment operator to assign the value to s
2 will create an instance of std::string on the heap and return a pointer to it, and will fail in the assignment because you can't assign a pointer to a non-pointer type
3 will build a std::string and initialize it from a const char*
On the number 1, you are creating a temporary string using the constructor and then assigning it to s.
Number 2 doesn't even compile.
On number 3, you are creating a new string and then assign a value to it.

C++: using & operator for pass-by-reference

I'm teaching myself C++, and in the process I'm writing simple little programs to learn basic ideas. With respect to "pass-by-reference", I'm confused why the following piece of code works (some of the code is just there to practice overloading constructors):
#include <iostream>
#include <string>
using namespace std;
class Dude
{
public:
string x;
Dude(); // Constructor 1
Dude(const string &a); // Constructor 2
};
Dude::Dude() : x("hi") {}
Dude::Dude(const string &a) : x(a) {}
int main()
{
Dude d1;
Dude d2 = Dude("bye");
cout << d1.x << endl;
cout << d2.x << endl;
return 0;
}
In "main()", I create an object "d2" of type "Dude", and use Constructor 2 to set "x" to be the string "bye".
But in Constructor 2's declaration, I told it to accept an address of a string, not a string itself. So why can I pass it "bye" (which is a string). Why don't I have to create a variable string, and then pass the address of that string to Constructor 2 of Dude?
This actually illustrates one of the coolest and most useful features of C++: Temporary variables. Since you specified that the string reference is const, the compiler allows you to pass a reference to a temporary value to that function. So, here's what's happening behind the scenes with Dude d2 = Dude("bye");:
The compiler determines that the best constructor to use is Dude::Dude(const string &). How this choice is made is a whole different topic.
However, in order to use that constructor you need a string value. Now, "bye" is a const char[4], but the compiler can trivially convert that to a const char *, and that can be turned into a string. So, an anonymous temporary variable (call it temp1) is created.
string::string(const char *) is invoked with "bye", and the result is stored in temp1
Dude::Dude(const string&) is invoked with a reference to temp1. The result is assigned to d2 (actually, it is assigned to another temporary variable and the copy constructor for Dude is invoked with a const reference to it and that is assigned to d2. But in this case the result is the same.)
temp1 is discarded. This is where the string destructor string::~string() is run on temp1
Control passes to the next statement
I think you're misunderstanding what the & operator does in this context. Taking the address of a variable (&var) is different from signifying that a parameter is to be passed as a reference (as you have, in const string &a).
What your code is actually doing is implicitly creating a new string object that's initialized with the string "bye", and then that object is passed by reference to the Dude constructor. That is, your code is essentially:
Dude d2 = Dude(string("bye"));
and then the constructor receives that string object by reference and assigns it to x via a copy constructor.
In this case, string has a constructor which takes a const char* and is not declared explicit, so the compiler will create a temporary string (created with string("bye"), the aforementioned constructor) and then your const string& is set to refer to that temporary.
Two things:
1) There's no such thing as an "address" in your code. const string& means "constant reference to a string".
You're possibly confused by the fact that the symbol & is also used in an entirely different context as the "address-of" operator to create a pointer: T x; T * p = &x;. But that has nothing to do with references.
2) You're not actually necessarily using the constructor that you claim for d2; rather, you're creating a temporary object with your constructor #2, and then you construct d2 via the copy constructor from the temporary. The direct construction reads Dude d2("bye");.
When you call second constructor with a string argument, a temporary variable which references a copy of the string will be created and passed to the constructor.
Constructor 2 is not taking an address to a string, const string& a means a constant reference to an std::string object. The reason why you can pass the constructor a string literal is because the std::string class contains a non-explicit constructor that takes a const char *. So the compiler implicitly converts your string literal to an std::string first before calling Constructor 2.
So the following 2 lines are equivalent
Dude d2 = Dude("bye");
Dude d2 = Dude( std::string("bye") );
Also, when writing constructors, prefer initializing member variables in the initializer list instead of within the body of the constructor
Dude(const string &a) : x(a) {}
temporaries can be bound to a const reference, probably for this reason.
When you call Dude("bye"), the compiler sees if that is a perfect match (char[4]) for any constructors. Nope. Then it checks certain conversions (char*) still nope. Then it checks user conversions, and finds that std::string can be implicitly constructed from a char* So it creates a std::string from the char* for you, and passes it by reference to Dude's constructor, which makes a copy. At the end of the statement Dude d2 = Dude("bye"); the temporary string is automatically destroyed. It would be irritating if we had to do the explicit casts ourselves for every single function parameter.
Variables passed to a reference parameter will automatically pass their address instead. This is nice, because it allows us to treat objects with value semantics. I don't have to think about passing it an instance of a string, I can pass it the value "bye".
Constructor #2 accepts a reference to a const string. That allows it to accept a reference to either a pre-existing object or a temporary object (without the const qualifier, a reference to a temporary would not be accepted).
std::string has a constructor that accepts a pointer to char. The compiler is using that to create a temporary std::string object, and then passing a reference to that temporary to your ctor.
Note that the compiler will only (implicitly) do one conversion like this for you. If you need more than one conversion to get from the source data to the target type, you'll need to specify all but one of those conversions explicitly.
While "&" is an addressof operator, when declared in as part of method definition/declaration, it means that the reference is passed to the method. The reference in this case is d2. Note that D2 is not a pointer, it is a reference. In the constructor, "a" represents the string object with contents "hi". This is a typical example of a pass by reference on a method in C++.

C++: Is "my text" a std::string, a *char or a c-string?

I have just done what appears to be a common newbie mistake:
First we read one of many tutorials that goes like this:
#include <fstream>
int main() {
using namespace std;
ifstream inf("file.txt");
// (...)
}
Secondly, we try to use something similar in our code, which goes something like this:
#include <fstream>
int main() {
using namespace std;
std::string file = "file.txt"; // Or get the name of the file
// from a function that returns std::string.
ifstream inf(file);
// (...)
}
Thirdly, the newbie developer is perplexed by some cryptic compiler error message.
The problem is that ifstream takes const * char as a constructor argument.
The solution is to convert std::string to const * char.
Now, the real problem is that, for a newbie, "file.txt" or similar examples given in almost all the tutorials very much looks like a std::string.
So, is "my text" a std::string, a c-string or a *char, or does it depend on the context?
Can you provide examples on how "my text" would be interpreted differently according to context?
[Edit: I thought the example above would have made it obvious, but I should have been more explicit nonetheless: what I mean is the type of any string enclosed within double quotes, i.e. "myfilename.txt", not the meaning of the word 'string'.]
Thanks.
So, is "string" a std::string, a c-string or a *char, or does it depend on the context?
Neither C nor C++ have a built-in string data type, so any double-quoted strings in your code are essentially const char * (or const char [] to be exact). "C string" usually refers to this, specifically a character array with a null terminator.
In C++, std::string is a convenience class that wraps a raw string into an object. By using this, you can avoid having to do (messy) pointer arithmetic and memory reallocations by yourself.
Most standard library functions still take only char * (or const char *) parameters.
You can implicitly convert a char * into std::string because the latter has a constructor to do that.
You must explicitly convert a std::string into a const char * by using the c_str() method.
Thanks to Clark Gaebel for pointing out constness, and jalf and GMan for mentioning that it is actually an array.
"myString" is a string literal, and has the type const char[9], an array of 9 constant char. Note that it has enough space for the null terminator. So "Hi" is a const char[3], and so forth.
This is pretty much always true, with no ambiguity. However, whenever necessary, a const char[9] will decay into a const char* that points to its first element. And std::string has an implicit constructor that accepts a const char*. So while it always starts as an array of char, it can become the other types if you need it to.
Note that string literals have the unique property that const char[N] can also decay into char*, but this behavior is deprecated. If you try to modify the underlying string this way, you end up with undefined behavior. Its just not a good idea.
std::string file = "file.txt";
The right hand side of the = contains a (raw) string literal (i.a. a null-terminated byte string). Its effective type is array of const char.
The = is a tricky pony here: No assignment happens. The std::string class has a constructor that takes a pointer to char as an argument and this is called to create a temporary std::string and this is used to copy-construct (using the copy ctor of std::string) the object file of type std::string.
The compiler is free to elide the copy ctor and directly instantiate file though.
However, note that std:string is not the same thing as a C-style null-terminated string. It is not even required to be null-terminated.
ifstream inf("file.txt");
The std::ifstream class has a ctor that takes a const char * and the string literal passed to it decays to a pointer to the first element of the string.
The thing to remember is this: std::string provides (almost seamless) conversion from C-style strings. You have to look up the signature of the function to see if you are passing in a const char * or a std::string (the latter because of implicit conversions).
So, is "string" a std::string, a c-string or a char*, or does it depend on the context?
It depends entirely on the context. :-) Welcome to C++.
A C string is a null-terminated string, which is almost always the same thing as a char*.
Depending on the platforms and frameworks you are using, there might be even more meanings of the word "string" (for example, it is also used to refer to QString in Qt or CString in MFC).
The C++ standard library provides a std::string class to manage and represent character sequences. It encapsulates the memory management and is most of the time implemented as a C-string; but that is an implementation detail. It also provides manipulation routines for common tasks.
The std::string type will always be that (it doesn't have a conversion operator to char* for example, that's why you have the c_str() method), but it can be initialized or assigned to by a C-string (char*).
On the other hand, if you have a function that takes a std::string or a const std::string& as a parameter, you can pass a c-string (char*) to that function and the compiler will construct a std::string in-place for you. That would be a differing interpretation according to context as you put it.
Neither C nor C++ have a built-in string data type.
When the compiler finds, during the compilation, a double-quoted strings is implicitly referred (see the code below), the string itself is stored in program code/text and generates code to create even character array:
The array is created in static storage because it must persist to be referred later.
The array is made to constant because it must always contain the original data (Hello).
So at last, what you have is const char * to this constant static character array.
const char* v()
{
char* text = “Hello”;
return text;
// Above code can be reduced to:
// return “Hello”;
}
During the program run, when the control finds opening bracket, it creates “text”, the char* pointer, in the stack and constant array of 6 elements (including the null terminator ‘\0’ at the end) in static memory area. When control finds next line (char* text = “Hello”;), the starting address of the 6 element array is assigned to “text”. In next line (return text;), it returns “text”. With the closing bracket “text” will disappear from the stack, but array is still in the static memory area.
You need not to make return type const. But if you try to change the value in static array using non constant char* it will still give you an error during the run time because the array is constant. So, it’s always good to make return constant to make sure, it cannot be referred by non constant pointer.
But if the compiler finds a double-quoted strings is explicitly referred as an array, the compiler assumes that the programmer is going to (smartly) handle it. See the following wrong example:
const char* v()
{
char text[] = “Hello”;
return text;
}
During the compilation, compiler checks, quoted text and save it as it is in the code to fill the generated array during the runt time. Also, it calculate the array size, in this case again as 6.
During the program run, with the open bracket, the array “text[]” with 6 elements is created in stack. But no initialization. When the code finds (char text[] = “Hello”;), the array is initialized (with the text in compiled code). So array is now on the stack. When the compiler finds (return text;), it returns the starting address of the array “text”. When the compiler find the closing bracket, the array disappears from the stack. So no way to refer it by the return pointer.
Most standard library functions still take only char * (or const char *) parameters.
The Standard C++ library has a powerful class called string for manipulating text. The internal data structure for string is character arrays. The Standard C++ string class is designed to take care of (and hide) all the low-level manipulations of character arrays that were previously required of the C programmer. Note that std::string is a class:
You can implicitly convert a char * into std::string because the
latter has a constructor to do that.
You can explicitly convert a std::string into a const char * by using the c_str() method.
As often as possible it should mean std::string (or an alternative such as wxString, QString, etc., if you're using a framework that supplies such. Sometimes you have no real choice but to use a NUL-terminated byte sequence, but you generally want to avoid it when possible.
Ultimately, there simply is no clear, unambiguous terminology. Such is life.
To use the proper wording (as found in the C++ language standard) string is one of the varieties of std::basic_string (including std::string) from chapter 21.3 "String classes" (as in C++0x N3092), while the argument of ifstream's constructor is NTBS (Null-terminated byte sequence)
To quote, C++0x N3092 27.9.1.4/2.
basic_filebuf* open(const char* s, ios_base::openmode mode);
...
opens a file, if possible, whose name is the NTBS s