I'm going to create a class to hold a long list of parameters that will be passed to a function. Let's use this shorter example:
class ParamList{
public:
ParamList(string& a_string);
string& getString(); //returns my_string
private:
string& my_string;
}
My question is this: my_string is private, yet I'm returning the reference to it. Isn't that called something like private pointer leaking in C++? Is this not good programming practice? I want callers of getString to be able to get the reference and also modify it.
Please let me know.
Thanks,
jbu
edit1: callers will use getString() and modify the string that was returned.
Returing a private reference is perfectly okay so long as:
A. That is a const reference, and you have documented when that reference can be invalidated or
B. That reference is intended to be modified (i.e. std::vector<T>::operator[])
While there are useful cases for returning a non-const reference you should usually avoid it. This is covered in Scott Meyers' Effective C++ (3rd Edition, Item 28): Avoid returning "handles" to object internals, if you'd like to take a look.
First off, you need to decide if the ParamList is going to own the string or just "know about it". The way you've written it, with string& my_string, means that it just has a handle onto someone else's string. In that case, it's not (much of) a problem for some to modify the string since ParamList doesn't own it in the first place!
If you want ParamList to have a full master copy of the parameters (depends on the problem you're trying to solve), do something like this:
class ParamList{
public:
ParamList(const string& a_string); // do a strcpy in here.
const string& getString(); //returns my_string
void setString(const string& new_string); //do a strcpy here too.
private:
string my_string;
}
Note that it's probably better to use set and get functions anyway rather than returning a non-const reference, just so ParamList can have a little more control over how its members are modified.
Related
I've come across a situation where I needed to set a property to a library object by means of a setter accepting const shared_ptr reference. Here is a simple example:
// library class
class WidgetProxy {
public:
void setName(const std::shared_ptr<std::string>& name);
// more methods
};
Suspecting nothing, I used it like this:
WidgetProxy widgetProxy(...);
auto name = std::make_shared<std::string>("Turing");
widgetProxy.setName(name);
// continue using `name`
Then I've found out that name had become empty after setName() call. Luckily, library source code was available and I was able to examine the implementation. It was roughly the following:
class WidgetImpl {
public:
void setName(std::string name)
{
name_ = std::move(name);
}
private:
std::string name_;
};
void WidgetProxy::setName(const std::shared_ptr<std::string>& name)
{
widgetImpl_.setName(std::move(*name));
}
So setName() moves out the string wrapped by the shared_ptr which is formally not prohibited since shared_ptr template argument is std::string and not const std::string.
My questions:
Is it a normal design to implement WidgetProxy::setName() like this?
Should a library user normally expect such behavior when they see a const shared_ptr<T>& function parameter?
Upd: The posted code snippets are much simplified. In the library there is a different type in place of std::string. I have also omitted checks for pointer validity.
Is it a normal design to implement setName() like this?
This implementation style is OK:
void setName(std::string name)
{
name_ = std::move(name);
}
The string is first copied by the function call, and the copied string is moved to the class member. The resulting code is as efficient than passing a reference to a string, and then copying to the data member.
This one is not. And I do not not recommend it.
void WidgetProxy::setName(const std::shared_ptr<std::string>& name)
{
widgetImpl_.setName(std::move(*name));
}
For 2 reasons. 1: why require a std::shared_ptr if the pointer is not kept? 2: The net result of the operation deletes the string held by the pointee. This affects all the other holders of the shared_ptr, some of which may need the value of the original string.
A more correct way to write this function, and the associated function call:
void WidgetProxy::setName(std::string name)
{
widgetImpl_.setName(std::move(name));
}
// call as:
if (strPtr)
proxy.setName(*strPtr); // with strPtr being a std::shared_ptr<std::string>
Should a library user normally expect such behavior when they see a const shared_ptr& function parameter?
No. This is a terrible way of coding a library. If the caller wishes to keep the string for any reason, he must create a shared_ptr with a copy of the original string. Plus, the library code does not even check if the shared_ptr holds a valid pointer! Very, very naughty.
You misunderstand what this means:
class WidgetProxy {
public:
void setName(const std::shared_ptr<std::string>& name);
};
setName takes a reference to a possibly mutable shared pointer that it does not have permission to modify. This shared pointer refers to a mutable string.
This means within setName, whenever control flows out of what is visible to the compiler, the pointer and validity of name could change (and, you should check that it does not).
The value pointed to by this non-mutable view of a possibly mutable shared pointer is fully mutable. You have full permission to modify it.
Some alternatives:
class WidgetProxy {
public:
void setName(std::shared_ptr<std::string> name);
};
This is a local shared pointer to a mutable string. It can only be modified locally, unless you leak references to it. The data referred to be be manipulated by any other code, and must be assumed to be modified whenever local context is left. It will, however, remain a valid pointer over the lifetime of the setName function unless you personally clear it.
class WidgetProxy {
public:
void setName(std::shared_ptr<std::string const> name);
};
this is a local shared pointer to a string you do not have mutation rights to. Someone else with a shared pointer to it could modify it if it is actually mutable at any point you leave local code, and should be presumed to be doing so.
class WidgetProxy {
public:
void setName(std::string name);
};
this is a local copy of a buffer of characters that nobody else can modify within the function, and that you own.
class WidgetProxy {
public:
void setName(std::string const& name);
};
this is a reference to a possibly mutable external std::string which must be presumed to be changed every time you leave local code in the function.
Personally, I see no reason why WidgetProxy is taking an arguments by shared_ptr or const&. It doesn't use the shared-ness of the argument, nor does it want the value to be remotely changed on it. It is a "sink" argument that it will consume, and the cost of moving the object is low.
WidgetProxy::setName should take a std::string. Sink arguments of cheap-to-move data should take by-value. And use of smart pointers here seems like a horrid idea; why complicate your life with shared_ptr?
It is perfectly fine for WidgetImpl::setName() to be implemented in this manner, as it is moving from local parameter.
It is simply a bug to implement WidgetProxy::setName this way, because you can't realistically expect the object managed by shared_ptr to be movable.
I have this constructor
Movie::Movie(char *&t, char *&d)
{
title = t;
director = d;
}
And in my main I have this:
Movie * movies = new Movie[movie_size];//creating array of class "Movie"
Movie *movieone=new Movie("MovieTitle", "Director");
movies[0]=movieone;
I get the following error:
no matching constructor for initialization of 'Movie' main.cpp line 14 C/C++ Problem
Isn't this correct, am I missing something? Thanks
When you do this
Movie("MovieTitle", "Director");
you are passing string literals, which collapse to const char*. But your constructor takes non-const references to char *:
Movie::Movie(char *&t, char *&d) { .... }
You cannot bind non-const references to something that is const. But in actual fact, it doesn't look like you need references at all. Just take the pointers by value, but make them point to somewhing const:
Movie::Movie(const char* t, const char* d)
This requires that you change title and director to const char*, which makes sense if you want to make them point to string literals.
But note that the design relies on the caller passing string literals, or passing pointers to objects that will outlive the given Movie instance. This can be made to work, but is quite fragile. The safer option would be for the Movie to "own" the data, which can easily be done by giving it std::string member variables.
As juanchopanza noted, the problem is that string literals get converted to type const char*, but your constructor only takes references to (non-const) char*, so it doesn't match.
A second point is that
Movie * movies = new Movie[movie_size];//creating array of class "Movie"
Movie *movieone=new Movie("MovieTitle", "Director");
movies[0]=movieone;
won't work. The first line creates an array of Movies (or tries to -- it won't work unless you have a default constructor), but the third line tries to set the first entry to a Movie*. Instead, you need to either a) create movies as an array holding pointers to Movies, or b) copy movieone by saying
movies[0] = *movieone;
Either way, you need to be very careful with regard to object lifetimes with this approach; it would be very easy to either leak memory on the one hand, or keep references to non-existent objects on the other.
A much better approach is to not (directly) use any dynamic memory allocation at all. You can use standard library facilities which hide it all for you, like this:
class Movie
{
public:
Movie(const std::string& title, const std::string& director)
: _title(title), _director(director) {}
private:
std::string _title;
std::string _director;
};
// later...
std::vector<Movie> movies;
movies.push_back(Movie("Jurassic Park", "Steven Speilberg"));
This is safer, easier to use, and no less efficient than your approach above.
Consider using std::string for store strings in class. For arguments pass better use const string& or const char*.
Movie *movieone=new Movie("MovieTitle", "Director");
Here you're passing const char*, but your constructor expects char*, so it doesn't match. Consider changing constructor to string or const char*
Forgive me if this has been asked before, I am sure it has but I couldn't find an answer I was happy with.
I am coming to cpp from a heavy Java background and would like to understand when to return a reference/pointer to an object rather than a copy.
for the following class definition:
class SpaceShip {
string name;
WeaponSystem weaponSystem; //represents some object, this is just an example, I dont have this type of object at all in my program
int hull;
string GetName() const {
return name;
}
WeaponSystem GetWeaponSystem() const {
return weaponSystem;
}
int GetHull() const {
return hull;
}
};
I know that returning a copy of things is expensive, I would think this means I want to avoid returning something like a string or weaponSystem by value, but an int by value is ok.
Is this right? I also know that I need to be aware of where things live in memory, does returning a reference to something in this class mean danger down the line if this object is destroyed and something still owns a reference to it's name?
On your last point, you definitely need to be a lot more careful about resource management in C++ than in Java. In particular, you need to decide when an object is no longer needed. Returning by reference has an effect of aliasing to the returned object. It is not noticeable when the object you are sharing is immutable, but unlike Java's Strings, C++ string are mutable. Therefore if you return name by value and then rename your SpaceShip, the caller would see the old name even after the renaming. If you return by reference, however, the caller will see a change as soon as ShaceShip is renamed.
When you deal with copying complex objects, you can decide how much is copied by providing a custom implementation of a copy constructor. If you decide to provide a copy constructor, don't forget the rule of three, and override the other two.
It "works" but you should have
const string& GetName() const {
It may also be beneficial to have the following also
const WeaponSystem& GetWeaponSystem() const {
Also, class is private by default, as such, your accessor functions are private.
the thing you have to know is every getter of your class must be prototype like that :
const <type> &className::getXXX() const
{
...
}
and every setter you make like that :
void className::setXXX(const <type> &)
{
...
}
Use reference when it's possible.
Sometimes, with complex object you can use pointer. That's depend on your code structure.
I have a class which basically is a text manager. It can draw text and whatnot. I basically want the color and text std::string to only be a constant reference. Would it then be alright to do
class TextManager {
const std::string &text;
void draw(const std::string &text) const;
public:
TextManager(const std::string &text)
{
this->text = text;
}
void someMethod()
{
draw(text);
}
};
I want when the class that owns an instance of TextManager's text changes, the change is reflected in the TextManager.
would I be better off using a pointer?
thanks
If you never need to re-seat the reference (i.e. refer to a different object), then it's fine. But in my experience, you'll inevitably find out later down the line that you need to be more flexible, in which case a reference is a pain. It may be better to go with a pointer from the start.
But note that you can only initialise a member variable of reference type in the constructor initialiser list. (Also, you probably want to declare that constructor as explicit).
This code doesn't compile. this->text = text doesn't do what you think it does - it's not like Java where assigning a reference is like changing the pointer. reference = value will actually invoke the copy operator, so it will copy the value of the rhs to the lhs, either as member-by-member copy or using the operator= if it was overridden. Since your text is const, you can't do that.
So in this case, you have to use a pointer - references cannot be modified once initialized.
EDIT: Just to explain ways in which you could use a reference:
const std::string &text = yourString;
or:
TextManager(const std::string &textRef)
: text(textRef)
{
}
That way, you have a permanent reference to whatever string you have.
Once you have sorted out the initialisation (which other comments can help you with), using a reference will let you do what you want. That is, changes to the referenced std::string will affect your class because they are the same std::string.
You can get similar behaviour using std::string const* instead of std::string const&. As Oli brought out, using a pointer is more flexible. Since a pointer can be null and can be updated using a pointer will allow you to define a default constructor and a (probably compiler generated) assignment operator. Which may not be important in this class but likely will be in some other class you will write (eg if you want to put objects of this class into a std::vector). So you probably are better off using a pointer internally. Though you may wish to still pass a reference to the constructor and take the address of it to initialise the member.
From the documentation of the StringPiece class in Chromium's source code:
// A string-like object that points to a sized piece of memory.
//
// Functions or methods may use const StringPiece& parameters to accept either
// a "const char*" or a "string" value that will be implicitly converted to
// a StringPiece.
//
// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
// conversions from "const char*" to "string" and back again.
Example of use:
void foo(StringPiece const & str) // Pass by ref. is probably not needed
{
// str has same interface of const std::string
}
int main()
{
string bar("bar");
foo(bar); // OK, no mem. alloc.
// No mem. alloc. either, would be if arg. of "foo" was std::string
foo("baz");
}
This seems like such an important and obvious optimization that I can't understand why it's not more widespread, and why a class similar to StringPiece is not already in the standard.
Are there any reasons why I shouldn't replace the use of string and char* parameters in my own code with this class? Is there anything like it already in the C++ standard libraries?
UPDATE. I've learnt that LLVM's source uses a similar concept: the StringRef class.
Bit later, but ...
Idea behind StringPiece is very good. The class can capture both std::string and const char * and pass them to a function. Here is an example:
void process(const StringRef s){
// do something
}
process("Hello"); // const char *
std::string s = "Hello";
process(s); // std::string
process(std::string("Hello")); // std::string rvalue
If function accepted std::string you actually creating a temporary object if you pass const char * and whole char is copied (for example with memcpy).
You also does not need to worry about livetime, because you are passing StringRef to function / method.
There are such class in:
Google - StringPiece
boost - boost::string_ref
LLVM - StringRef
My own (incomplete) implementation can be seen here:
https://github.com/nmmmnu/HM3/blob/master/include/stringref.h
Update 2016:
In C++17, there are std::string_view. I did not study it in details, but in general it has same idea. Also similar to mine implementation it have constexpr c-tor, so you can create objects at compile time and use it along with other constexpr functions.
The question has been very well answered already, but to give some more context, the StringPiece pattern is very widely used internally at Google and has been for many years. It is strongly recommended in Google coding guidelines, which is almost surely why Chrome (and subsequently Chromium) have adopted it.
StringPiece keeps a pointer to the string data that is passed to its constructor. Thus it relies on the lifetime of that string to be longer than the StringPiece. This is guaranteed if you are using StringPiece as a function parameter; not so if you're keeping a copy of the string in a class member for example.
It's not quite as universal as std::string, and it's definitely not a replacement for it.
The standard is trying to move away from const char* in favor of string altogether, so adding more options for conversion is useless.
Also note that a nice formed program should use either string or const char* all around ;).
StringPiece is great but the pieces aren't null terminated. If you need to pass to lower level interfaces that take null terminated strings, you need to copy the stringpiece into a null terminated string.
(Not everyone has bought into STL or std::string.)
Because why bother? With copy elision and/or pass by reference, memory allocations for std::string can usually be avoided as well.
The string situation in C++ is confusing enough as it is, without adding still more string classes.
If the language was to be redesigned from scratch, or if backwards compatibility wasn't an issue, then this is one of many possible improvements that could be done to string handling in C++. But now that we're stuck with both char* and std::string, adding a stringref-style class into the mix would cause a lot of confusion, with limited benefit.
Apart from this, isn't the same effect achieved more idiomatically with a pair of iterators?
If I want to pass a sequence of characters, whether they belong to a string or a char*, why shouldn't I just use a pair of iterators to delimit them?