Does declaring a private string as a reference make a difference? - c++

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.

Related

If I pass some string by reference to a Class Constructor then why is the address of the string(class member) different from the actual string

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)
{}
};

Return a "const char*" as std::string& from a class method

I have a C-Libraray function returning a C-String
const char* myFuncC(struct myS *);
Now I write a class mapper:
class myC {
private:
struct myS * hdl
...
public:
const char* myFunc() {
return myFuncC(hdl);
}
// want to have...
const std::string& myFuncS() {
return ??? myFuncC(hdl);
}
}
Now I would like to return a: const std::string& and I don't want to copy the C-String pointer data
How I do this?
update
Is there a C++ Class who act like a const C++-String class and using a const C-String pointer as string source ?
I would call this class a C++-String-External … External mean … using a external source as storage… in my case a "const char *"
std::string, by definition, owns its memory and there’s nothing we can do to change this. Furthermore, by returning a reference you’re creating a dangling reference to a local variable (or a memory leak).
However, the idea of encapsulating contiguous character ranges in a string-like interface without copying is such a common problem that C++17 added a new type, std::string_view, to accomplish exactly that. With it, your code can be written as:
std::string_view myFuncS() {
return {myFuncC(hdl)};
}
A string_view isn’t a string but it has a very similar API and the idea is to use it instead of many current uses of string const&. It’s perfect for the job here.
Note that we are returning a copy, to avoid a dangling reference. However, this does not copy the underlying memory, and is cheap — almost as cheap as returning the reference.
Simply return a new string:
std::string myFuncS() {
return std::string(myFuncC(hdl));
}
If you really need a refence or a pointer then allocate the string on the heap and return a pointer to it:
std::unique_ptr<std::string> myFuncS() {
return std::unique_ptr<std::string>(new std::string(myFuncC(hdl)));
}

Returning const std::string& vs const char* for class member

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)

Do arguments get copied if they're stored

Are arguments copied if they are stored in the following cases
Literal string passed:
std::string globalStr;
void store(const std::string &str)
{
globalStr = str;
}
store("Literal");
Variable string passed:
std::string globalStr;
void store(const std::string &str)
{
globalStr = str;
}
store(varStr);
And what if globalStr stores reference
std::string &globalStr;
void store(const std::string &str)
{
globalStr = str;
}
store("Literal"); //Should this cause any problem?
store(varStr);
Does C++ optimize to prevent making unnecessary copies in any of the above cases?
Does C++ optimize to prevent making unnecessary copies in any of the above cases?
No.
The stardard just guarantees that, in the 1st and 2nd cases, the value of str will be copied to globalStr, using std::string::operator=().
Depending on the implementation of your STL, if std::string uses a copy-on-write optimization, a deep copy might be avoided.
The 3rd case will not compile, as a reference cannot be reassigned after it has been initialized.
In the first two examples, you are binding a reference: void store(const std::string &str), that in itself does not copy.
But in the statement where you are assigning to a global variable: globalStr = str; - It does make a copy of it.
In your third example this does not compile: std::string &globalStr; - reference needs to be initialized!
You can try to set up for a move by writing:
void store(std::string &&str)
{
globalStr = std::move(str);
}
store(std::move(varStr));
As in any case involving compiler optimization: it depends on the compiler.
If the compiler is programmed to (and can in the situation given to it) prove to itself that a copy is unnecessary, it won't perform that copy.
For your particular question, neither of the first two methods could perform unnecessary copies anyway, because the inputs are passed by reference into the store function. They are copied when you assign them to globalStr, but that copy is necessary because globalStr is a value variable; it is its own copy, and therefore cannot point to some other copy.
And this line:
std::string &globalStr;
can't compile. A reference must be assigned something to refer to at the time it is declared.

Return std::string as const reference

I have a doubt on returning std::string as const reference.
class sample
{
public:
std::string mString;
void Set(const std::string& s)
{
mString = s;
}
std::string Get()
{
return mString;
}
};
In the Set function I am passing the std::string as const reference, const because its value is not changing inside the function.
And In Get function, actually I am confused here. Return std::string as value makes more sense. But I am not sure that, by passing the string as const reference makes any advantages. By returing string as reference will increase the exectuion speed, I think So, but I am not sure. But returning it as 'const makes any benefit for this?
The problem of deciding how to return a non-trivial object from some sort of a container is actually non-trivial:
If the class from which you return your value imposes any sort of constraint on the object, you can't return a non-const reference because it would loose the possibility to enforce its invariants. Clearly, returning an object by non-const reference is only viable if object the member function is called on is also non-const.
Exposing a const reference to an object would avoid the problem with the invariants but still implies that an object of the corresponding type is actually kept internally as an implementation detail.
Returning an object by value may incur a significant cost for copying the object.
If you class is further a viable monitor you definitely want to return the object by value because otherwise the object can be mutated before the caller had any chance to copy it.
Basically, none of the choices is ideal. When in doubt, I return by value unless the object is known to be expensive to copy in which case I might return by const&.
Returning by reference or const reference has no speed difference - both are very fast as they just return a reference to the original object, no copying is involved.
An object returned by (non-const) reference can be modified through that reference. In your specific example, mString is public, so it can be modified anyway (and directly). However, the usual approach with getters and setters (and the primary reason for their introduction) is encapsulation - you only allow access to your data members through the getter/setter, so that you can detect invalid values being set, respond to value changes and just generally keep the implementation details of your class hidden inside it. So getters normally return by const reference or by value.
However, if you return by const reference, it binds you to always keep an instance of std::string in your class to back up the reference. That is, even if you later want to redesign your class so that it computes the string on the fly in the getter instead of storing it internally, you can't. You'd have to change your public interface at the same time, which can break code using the class. For example, as long as you return by const-reference, this is perfectly valid code:
const std::string *result = &aSample.Get();
This code will of course produce a dangling pointer no longer compile if Get() is changed to return by value instead of const reference. (thanks to Steve Jessop for correcting me)
To sum up, the approach I would take is to make mString private. Get() can return by value or by const-reference, depending on how certain you are that you'll always have a string stored. The class would then look like this:
class sample
{
std::string mString;
public:
void Set(const std::string &s)
{
mString = s;
}
std::string Get() const
{
return mString;
}
};
The most common thing to do here would be to return the value as a const-reference, then you can use a reference or copy the value as necessary:
const std::string& Get() const
{
return mString;
}
sample mySample;
const std::string &refString = mySample.Get(); // Const-reference to your mString
const std::string copyString = mySample.Get(); // Copy of your mString
If you really need to return a copy of the string, then you can avoid copying the string return value by utilising "The Most Important Const":
sample mySample;
const std::string &myString = mySample.Get();
// myString is now valid until it falls out of scope, even though it points to a "temporary" variable
Return it as a reference. If a copy is needed, it certainly can be made from that reference.