I decided to see if assigning a reference to a member would make a member a reference. I wrote the following snippet to test it. There's a simple class Wrapper with an std::string as a member variable. I take take a const string& in the constructor and assign it to the public member variable. Later in the main() method I modify the member variable but the string I passed to the constructor remains unchanged, how come? I think in Java the variable would have changed, why not in this code snippet? How exactly do references work in this case?
#include <iostream>
#include <string>
using namespace std;
class Wrapper
{
public:
string str;
Wrapper(const string& newStr)
{
str = newStr;
}
};
int main (int argc, char * const argv[])
{
string str = "hello";
cout << str << endl;
Wrapper wrapper(str);
wrapper.str[0] = 'j'; // should change 'hello' to 'jello'
cout << str << endl;
}
To assign a reference in a constructor you need to have a reference member
class A{
std::string& str;
public:
A(std::string& str_)
: str(str_) {}
};
str is now a reference to the value you passed in. Same applies for const refs
class A{
const std::string& str;
public:
A(const std::string& str_)
: str(str_) {}
};
However don't forget that once a reference has been assigned it can not be changed so if assignment requires a change to str then it will have to be a pointer instead.
Because Wrapper::str is not a reference, it's an independent object. So when you do str = newStr, you're copying the string.
class Wrapper
{
public:
string& str;
Wrapper(string& newStr) : str(newStr) {}
};
Note, you cannot accept a const string& and store it in a string&, you would lose const-correctness in doing so.
You need to use an initializer and declare str as a reference as in:
class Wrapper {
public:
string &str;
Wrapper(string& newStr)
: str(newStr) {
}
};
The way you're writing it, all you are doing is copying the value of the reference you pass to the constuctor. You're not saving the reference. By declaring a reference as a class member and initializing it with a reference to another string instance, you will get the behavior you're looking for.
You should declare Wrapper::str as a string&, not as a string.
Your main body variable is std::string.
Your parameter variable is const std::string&.
The const in references are always "low level const", meaning it modifies the type of object not the actual object.
In contrast a "top level const" modifies an actual object. Read C++ Primer on Top level const for clarification.
Here is how your assignment looks like when you pass arguments:
const std::string& = std::str; //Values are ommited
// i.e const std::string newStr = std::string str
You are initializing a const type reference with a non-const value which is acceptable. You are not supposed to change the value of std::string str using that reference. And, if you try changing the value of newStr inside the constructor, you will get a compilation error.
Next you're doing another assignment inside the constructor which is also acceptable:
std::string = const std::string
The fact that wrap.str[0] didn't change str of main is that, although a reference was used to instantiate class str, class str has its own object and is not linked to main str. Using that reference in a parameter just links that parameter to main str; not main str to class str.
If your class variables were referenced, then it could have changed.
Related
I don't understand, If I pass a string by ref. to a function then the address of actual string passed will be same as that of formal string in this function. Any changes to either will impact on either of the two. But it is not same for passing string by ref. to a class ctor. Why is that? Although I do understand, different objects must have different addresses.
Class Abc
{
private:
std::string s;
public:
Abc(std::string str)
: s(str)
{}
};
void fun(std::string& str)
{ str [0]='i';} // changes content in st
int main()
{
std::string st = "hello";
Abc obj(st);
//if we change st shouldn't it change s in obj
st[0] = 'j';
fun(st);
}
Your member variable std::string s; is not a reference, it's a plain string. So when you initialize it it gets constructed by copying the string you initialize it with.
You are copying the string by value in the constructor.
This is how you would store the reference:
class Abc{
std::string& str;
public:
Abc(std::string& s): str(s){}
};
void foo(){
std::string str;
Abc abc(str);
str = ”Hello World”;
/// abc.str is now also hello world
}
But now, beware about lifetimes!
By passing the object by reference to the object in the constructor, the lifetime is not extended, meaning when str goes out of scope, abc.str points to invalid memory -> undefined behavior.
C++, unlike other languages such as python is a value-based language as in that assignments copy by default unless you do extra tricks like move, taking the address or taking a reference (which is a similar thing to taking the address).
In your example, you're not passing the string as a reference to the constructor.
Abc(std::string str)
should be
Abc(std::string& str)
If you want the value change of the original string to reflect into the member variable in class(or the other way around). Then you need to make member variable as reference. Your class should look like
Class Abc
{
private:
std::string& s;
public:
Abc(std::string& str)
: s(str)
{}
};
Considering the following class example :
class AClass
{
std::string myString;
public:
...
}
With one of the following accessors :
const std::string& GetMyString() const
{
return myString;
}
versus
const char* GetMyString() const
{
return myString.c_str();
}
Taking into account that myString is initialized once and is never changed, which accessor is better? The first one, or the second one? In what situation one of them is more suitable than its neighbor?
The version returning const std::string& covers a superset of the use cases for returning const char* (after all, it can be converted to the latter by calling .c_str() on the return value), with no added weaknesses. Given that std::string is more flexible in other ways, I'd prefer const std::string& of the two options.
That said, they're both awkward if the owning object in question isn't immortal; even if the string is never changed, if the object itself disappears, the reference to its string is now invalid. If that's a possibility, you might want to either:
Return by value
or
Use a std::shared_ptr<std::string> member and return that (so the lifetime of the string is no longer tied to the lifetime of the object that created it)
While i was reading my c++ book and programming some of the examples, a question came to my mind.
...
private:
const string someString;
public:
MyClass(const string& someString) : someString(someString) {}
const string& getSomeString() const { return someString; }
...
Does declaring someString as a reference actually make a difference?
...
private:
const string& someString;
public:
MyClass(const string& someString) : someString(someString) {}
const string& getSomeString() const { return someString; }
...
If so, what are the advantages/disadvantages or use cases (since both examples compile fine)?
The latter will easily lead to dangling references as it just points to some object not controlled by your class. So I would avoid that. (As always, unless you have a good reason.)
Also a notable difference: In the second case, the string in your class "will change" if the string used to construct it does, as you are only referencing it. This would not happen in the first case as you own your own copy of the string.
If you want a string of your own, don't store a reference to somebody else's string.
In the second case, the lifetime of the string you're storing a reference to must exceed that of the object you're storing it in.
For example,
MyClass instance("bad");
would leave a dangling reference in the member.
You also have the less fatal but confusing spooky action at a distance:
std::string s = "Hello";
MyClass instance(s);
s = "World";
std::cout << instance.getSomeString(); // Prints 'World'
Reference members are very rarely a good solution, in my experience.
The string referenced in the constructor comes from outside the class. The reference member is only valid as long as the original string is valid. Consider this example:
MyClass *p;
{
string temp = "hello";
p = new MyClass(temp);
}
cout << p->getSomeString(); // reference to destroyed object
This code is wrong because the string temp which is referred to in the class no longer exists.
The problem can manifest more subtly.
const char *text = "Hello";
MyClass c(text);
cout << c.getSomeString(); // reference to destroyed object
This code is also wrong because the temporary std::string object created for the constructor call no longer exists by the time of the next line.
If you declare someString as a const string it will contain that value that is passed to it in the constructor.
However, with someString being a const string&, it holds the address of a string which is stored somewhere outside of the class, which the class can't guarantee will still exist at any point in the future, so you should avoid this one.
If someString should not be an observer, then by storing a reference you're implying the wrong semantics. I would avoid that.
Otoh, if it should be an observer then you obviously do have to store a reference.
I have a class like that.I want to use reference for string but it doesnt work.How can i use string& ?
#include <string>
using namespace std;
class T
{
public:
T(string& s);
private:
string s;
};
T::T(string& s)
{
this->s = s;
}
int main(void)
{
T t("Test Object");
return 0;
}
Error : 'T::T(std::string &)' : cannot convert parameter 1 from 'const char [12]' to 'std::string &'
Use const :
class T
{
public:
T(const string& ss);
private:
string s;
};
T::T(const string& ss) : s(ss)
{
}
"Test Object" will be constructed as a const string before passing to T's constructor, so the constructor has to accept a const string.
You're not passing in a std::string. As it says, it can't convert from const char array to string-ref. Change the constructor to take a const-ref and it'll work. This is because a temporary string will have to be created from the char-array, and you can only make const-references to temporaries to stop you getting confused (if you modify the temporary, the changes are just going to be discarded, so the language stops you from doing that).
In C++98/03, if you have a class that is not cheap to copy (e.g. an int or a double are cheap to copy, a std::string isn't, since its copy can involve allocating new heap memory, copying characters from source to destination, etc.), then the rule is to pass by const reference const std::string&:
class T
{
public:
T(const string& s); // <--- const string&
private:
string m_s;
};
And then in constructor do:
T::T(const string& s)
: m_s(s)
{}
However, in C++11, where move semantics is available, the new rule seems to be: if you need a copy (and the object is cheap to move, as it normally should be), pass by value and move from the value:
T::T(string s) // pass by value
: m_s( std::move(s) ) // and move from the value
{}
(The optimal thing would be to offer a couple of overloads, passing by const & and passing by value, but probably this is not necessarily in all applications, but only when you need to squeeze performance.)
Note that when you don't need a copy, and just need to observe the parameter, the usual C++98/03 pass by const & rule is still valid.
The constructor you defined takes a std::string by reference:
T::T(std::string& s)
{
this->s = s;
}
thus the most straightforward thing to do would be to create a std::string object, that will be passed to this constructor:
std::string s("Test Object");
T t(s);
But since your constructor doesn't change the std::string you pass to it (it is just used to set the value of T's data member) you should pass const reference: T::T(const string& s). Also instead of letting the data member s being constructed and assigning another string into it later, it would be better if you construct this member directly within an initialization list:
T::T(const std::string& str) : s(str) { }
References need to be intialized using the initialiser-list of the constructor. Change your constructor to:
T::T(string& s) : s(s)
{
}
Additionally define your member s as std::string& s to be able to take a reference.
Maybe change the name of s to avoid ambiguities.
See this entry on SO.
When I pass an object to another object as a const reference, is there a copy made? I always assumed since I passed in the object by reference the member object itself was actually the object I passed in and not a copy. I made a test program that causes the passed in reference object to be destroyed at the end of scope, but it doesn't crash as I expected. Is this a bug waiting to happen or is the object getting copied?
#include <iostream>
#include <string>
class Something
{
public:
Something(const std::string& str) : mStr(str) {}
const std::string& str() const
{
return mStr;
}
private:
std::string mStr;
};
int main()
{
Something* something;
{
std::string temp = "Testing.";
something = new Something(temp);
}
std::cout<<something->str()<<"\n";
delete something;
return 0;
}
Is that std::string still valid or is it deleted? (In the object itself)
The data member mStr is of type std::string (it's an object, not a reference).
Therefore, when you initialize it in the constructor's initialization list (via : mStr(str)), the argument, which was passed by reference, is copied. In your example, only the initialization causes a copy to be made: if you removed the initialization, no copies would be made.
Contents of temp variable is copied to mStr. The code is valid.
You are correct, except you are missing the fact that mStr(str) makes a copy of str. This copy is destroyed in ~Somthing();