Getter and instance variable, reference, pointer, or object? - c++

Let's say I have a class Position:
class Position{
public:
Position(int x, int y) : x(x), y(y) {}
int getX(void) { return x; }
int getY(void) { return y; }
private:
int x;
int y;
};
and a class Floor:
class Floor {
public:
Floor(Position p) : position(p) { }
private:
Position position;
};
If I were to add a getter like getPosition(void), what should it return?
Position? Position*? Position&?
And should I have Position position or Position* position as instance variable? Or Position& position?
Thanks.

By default, if you want a value of type T, use a data member of type T. It's simple and efficient.
Position position;
Usually, you will want to return this by value or by reference to const. I tend to return objects of fundamental type by value:
int getX() const
{
return x;
}
and class objects by reference to const:
Position const& getPosition() const
{
return position;
}
Objects that are expensive to copy will often benefit from being returned by reference to const. Some class objects may be quicker to return by value, but you'd have to benchmark to find out.
In the less common case where you want to allow the caller to modify your data member, you can return by reference:
Position& getPosition()
{
return position;
}
However, it is usually better prevent direct access to class internals like this. It gives you more freedom to change implementation details of your class in the future.
If you need to dynamically allocate the value for some reason (e.g. the actual object may be one of a number of derived types at determined at runtime), you can use a data member of std::unique_ptr type:
std::unique_ptr<Position> position;
Create a new value using std::make_unique:
Floor() :
position(std::make_unique<FunkyPosition>(4, 2))
{
}
Or move in an existing std::unique_ptr:
Floor(std::unique_ptr<Position> p) :
position(std::move(p))
{
}
Note that you can still return by value or reference to const:
Position const& getPosition() const
{
return *position;
}
That is, as long as position cannot contain nullptr. If it can, then you may want to return a pointer to const:
Position const* getPosition() const
{
return position.get();
}
This is a genuine use of raw pointers in modern C++. Semantically, this communicates to the caller that the value returned is "optional" and may not exist. You should not return a reference to const std::unique_ptr<T>, because you can actually modify the value it points to:
std::unique_ptr<Position> const& getPosition() const
{
return position;
}
*v.getPosition() = Position(4, 2); // oops
In addition, returning a std::unique_ptr would once again expose unnecessary implementation details, which you should prefer not to do.
It is also possible for multiple objects to own the same dynamically-allocated object. In this case, you can use std::shared_ptr:
std::shared_ptr<Position> position;
Create a new value using std::make_shared:
Floor() :
position(std::make_shared<FunkyPosition>(4, 2))
{
}
Or copy/move in an existing std::shared_ptr:
Floor(std::shared_ptr<Position> p) :
position(std::move(p))
{
}
Or move in an existing std::unique_ptr:
Floor(std::unique_ptr<Position> p) :
position(std::move(p))
{
}
Only once all std::shared_ptrs pointing to an object have been destroyed is the object itself destroyed. This is a far heavier-weight wrapper than std::unique_ptr so use sparingly.
In the case where your object is referencing an object it doesn't own, you have several options.
If the referenced object is stored in a std::shared_ptr, you can use a data member of type std::weak_ptr:
std::weak_ptr<Position> position;
Construct it from an existing std::shared_ptr:
Floor(std::shared_ptr<Position> p) :
position(std::move(p))
{
}
Or even another std::weak_ptr:
Floor(std::weak_ptr<Position> p) :
position(std::move(p))
{
}
A std::weak_ptr does not own the object it references, so the object may or may not have been destroyed, depending on whether all its std::shared_ptr owners have been destroyed. You must lock the std::weak_ptr in order to access the object:
auto shared = weak.lock();
if (shared) // check the object hasn't been destroyed
{
…
}
This is super safe, because you cannot accidentally dereference a pointer to a deleted object.
But what if the referenced object is not stored in a std::shared_ptr? What if it is stored in a std::unique_ptr, or isn't even stored in a smart pointer? In this case, you can store by reference or reference to const:
Position& position;
Construct it from another reference:
Floor(Position& p) :
position(p)
{
}
And return by reference to const as usual:
Position const& getPosition() const
{
return position;
}
Users of your class have to be careful though, because the lifetime of the referenced object is not managed by the class holding the reference; it's managed by them:
Position some_position;
Floor some_floor(some_position);
This means that if the object being referenced is destroyed before the object referencing it, then you have a dangling reference which must not be used:
auto some_position = std::make_unique<Position>(4, 2);
Floor some_floor(*some_position);
some_position = nullptr; // careful...
auto p = some_floor.getPosition(); // bad!
As long as this situation is carefully avoided, storing a reference as a data member is perfectly valid. In fact, it is an invaluable tool for efficient C++ software design.
The only problem with references is that you cannot change what they reference. This means that classes with reference data members cannot be copy assigned. This isn't a problem if your class doesn't need to be copyable, but if it does, you can use a pointer instead:
Position* position;
Construct it by taking the address of a reference:
Floor(Position& p) :
position(&p)
{
}
And we return by reference to const as before:
Position const& getPosition() const
{
return *position;
}
Note that the contructor takes a reference, not a pointer, because it prevents callers from passing a nullptr. We could of course take by pointer, but as mentioned earlier, this suggests to the caller that the value is optional:
Floor(Position* p) :
position(p)
{
}
And once again we may wish to return by pointer to const:
Position const* getPosition() const
{
return position;
}
And I think that's it. I spent far longer writing this (probably unnecessarily detailed) answer than I intended to. Hope it helps.

Related

Chaining return-by-reference calls to access underlying data

I worked previously only with pointers and i don't know if references behave the same way in return-statements.
Can I chain multiple return-by-reference methods to access data without fearing for a "dangling" reference which contains garbage? Or would the reference go out of scope before passing it to the function-call above?
See the code below if this is a valid way to pass references (we assume that the indexes are all in range)
struct Data {
//Non-Trivial Data
}
class Container {
public:
Data& get(int index) { return m_Collection[index]; }
private:
Data[100] m_Collection;
}
class ContainerCollection {
public:
Data& get(int containerindex, int dataindex) { return m_Container[containerindex].get(dataindex); }
private:
Container[3] m_Container;
}
Yes, this is a valid way of chaining references.
You can chain references within functions without worrying about dangling references (which you should worry with pointers). As long as m_Container exists, the reference is available, after that it points to NULL.

Copy reference to class member resource

I have a class defined like:
class Foo
{
public:
void ReturnReferenceToMember(???)
{
??? // Give caller a reference to member variable m_x
}
private:
std::unordered_map<int, int> m_x;
}
What's the C++ way to implement the ????
I don't want to copy the internals of the map, and I also want to keep being able to use the map inside the class. I don't understand move mechanics that well, but it seems to me like the second requirement negates std::move...
It is not possible to pass a reference that can be changed. That would mean passing a pointer to a reference, or a reference to a reference - both of those are invalid in C++.
The usual approach is to return a reference, not (since it can't be done) to pass one in the hope of it being changed.
std::unordered_map<int, int>& ReturnReferenceToMember()
{
return m_x;
}
If you expect to pass an argument that can be changed in order to permit the caller to access class internals, it is necessary to use a pointer to a pointer
// double indirection needed as pointers are passed by value too
void ReturnPointerToMember(std::unordered_map<int, int> **p)
{
*p = &mx;
}
or a reference to a pointer
void ReturnPointerToMember(std::unordered_map<int, int> *&p)
{
p = &mx; // note the missing asterisk compared with passing pointer to pointer
}
If you don't wish the caller to change class internals, it is necessary to const-qualify the above in various ways.
void ReturnReferenceToMember(std::unordered_map<int, int>** outMap)
{
*outMap = &m_x;
}
void ReturnReferenceToMember(const std::unordered_map<int, int>** outMap) const
{
*outMap = &m_x;
}
I overloaded it on const intentionally, so you can use either form depending on whether the Foo you have is const.
As you can see, we are not really using references here, because those cannot be reseated, but the caller can still use them:
std::unordered_map<int, int>* mapPtr = nullptr;
ReturnReferenceToMember(&mapPtr);
if (mapPtr)
{
std::unordered_map<int, int>& mapRef = *mapPtr;
// now you have your reference
}

what does T const & mean in return types of functions?

What kind of a return type does the following function prototype have?
int const& myfunctionname(int i)
I am not getting the use of the const and & here. If I am returning objects/arrays/ basically non-local variables, should I use a pointer or a reference if I want to return the location of the object?
It means you're returning a reference to a value that cannot be changed.
If you return a reference to a local variable then you're going to have a problem, as it will reference something that no longer exists:
int const &GetValue()
{
int x = 10;
return x; // THIS IS BAD!
}
Also, it doesn't really make sense to return const references to the integral types (int, long, etc) as they take pretty much the same space as the reference in the first place. However if you do then it's not a big deal.
If you're returning non-local data (ie member data) then use references if the data will always exists, and use pointers if the data may not exist, so that NULL indicates the absence of the data. For example:
const std::string &User()const
{
return m_Username;
}
int Age() const
{
return m_Age; // Pointless to use a reference here
}
const Person *Partner()const
{
return m_Parter; // User may not have a partner, so pointer
}
Make the pointer or reference const if you don't want the caller to be able to modify the object returned.
Such function can return a reference to a member variable of the class it belongs to, but the calling function will not be able to change it. For example:
class Foo
{
public:
Foo() {boo=5;}
public:
int const& myfunctionname(int i) {boo += i; return boo;}
private:
int boo;
}
void func(Foo& foo)
{
const int& a = foo.myfunctionname(6);
a += 7; // Compilation error
int b = a+7; // OK
}
Supplemental:
The most common example of returning a constant value by reference, is probably operator++().

Preserve constness on assigning an object

Is it possible to preserve constness of an object when assigning? Consider a class holding pointer to dynamic memory, a soft copy is expected on assignment. (There is some more complex logic including reference counting, but it is not relevant for the question).
class Vec {
private:
int *data_; // dynamic array
public:
int * Data() { return data_;} // access for modification
const int * Data() const { return data_;} // read only access
Vec subVec(int pos) { // create writable submat
Vec ret;
ret.data_ = &data_[pos];
return ret;
}
const Vec subVec(int pos) const { // create read only submat
Vec ret;
ret.data_ = &data_[pos];
return ret;
}
};
void processVec(const Vec & src) {
src.Data()[0] = 1; // not allowed, since src const
Vec subVec = src.subVec(2); // call const version of subVec and create
// a soft copy - wrapper for a part of array.
// Assignment creates a copy, which removes constness
subVec.Data()[0] = 1; // allowed! Since subVec is a copy, but modifies
// identical dynamic memory from the wrapped src!
}
I would like subVec.Data()[0] = 1; to fail, since it is supposed to remain const.
Your problem is, that you are defining a smart pointer to some object, but you confuse constness of the smart pointer (the analogue to Foo* const bar;) with constness of the object (the analogue to const Foo* bar;). What you need, is to separate the constness of the pointer from the constness of the object.
You can relatively easily do so by using two smart pointer classes instead of one: A base class, that implements the smart pointer for a const object, and a derived class that implements the smart pointer for a non-const object.
With that, you can always demote your smart pointers to the "const" base class, make copy free "const" copies, etc. You might even add a constructor to the derived, non-"const" smart pointer class that takes a smart pointer of the "const" base class to make a deep copy of the data.
I would mimic the iterator and const_iterator idiom by creating a different return type when working with const or non-const object. Here an example implementing just a different type for const call to subVec:
class Vec {
// ...
public:
// ...
const int * Data() const { return data_;} // read only access
Vec subVec(int pos) { // create writable submat
Vec ret;
ret.data_ = &data_[pos];
return ret;
}
class SubVec
{
private:
const int* data_;
public:
SubVec(const int* data) : data_(data) {}
const int * Data() const { return data_;} // read only access
};
const SubVec subVec(int pos) const { // create read only submat
SubVec ret(&data_[pos]);
return ret;
}
};
This will generate the error you want in the const case and will compile in the non const case. Using auto allows a easier code writing for your code user.
IIUC, the elements which are either const or non-const are assigned at run-time, hence the type system (which is static) can not help you with that. You'd need to store some boolean flag with each element and perform a run-time check on assignment to see if it allowed or not.

How do I assign a data object with const members?

Hope this is not a duplicate. If so, please point me to it in a comment and I'll remove the question again.
I have a data object with data that's only valid in a bundle - i.e. there's no sense in changing the value of one member without invalidating the other members.
This data object describes some image information:
struct ImageInfo
{
ImageInfo(const double &ppix, ...)
: PpiX(ppix),
...
{ }
const double PpiX;
const double PpiY;
const int SizeX;
const int SizeY;
};
In my image object I have a non-const member of type ImageInfo:
class MyImageObject
{
...
private:
ImageInfo mMyInfo;
}
I want to be able to change mMyInfo at runtime, but only so that it will take a new ImageInfo(...) instance.
In the MyImageObject::Load() function, I'd like to read this data from the file info and then create an ImageInfo instance with the correct set of data:
double ppix = ImageFile.GetPpiX();
...
mMyInfo = ImageInfo(ppix, ...);
But I couldn't manage to write a valid assignment operator (copy constructor is possible of course). My solution left mMyInfo empty, because I didn't reference this:
ImageInfo operator=(const ImageInfo &other)
{
// no reference to *this
return ImageInfo(other);
}
Out of curiosity I'd like to know how the assignment operator for such a class would need to look like.
I'm using plain C++.
EDIT
Possible solutions (the goal is to keep the data transportable, but coherent):
Use private members together with Get...() functions -> simple, but I'd like to avoid the parentheses.
Store a pointer to ImageInfo: ImageInfo *mpMyInfo; (I'd like to avoid the heap.)
Use serialization and store the serialized ImageInfo, then create local instances from the serialized data.
I don't think you can have const member variables that aren't static. If you need const variables that change with the instance, you could do something like this:
struct ImageInfo
{
private:
double myPpiX;
double myPpiY;
int mySizeX;
int mySizeY
public:
ImageInfo(const double &ppix, ...)
: myPpiX(ppix),
PpiX(myPpiX),
...
{ }
ImageInfo( const ImageInfo &other)
: myPpiX( other.myPpiX),
PpiX(myPpiX)
...
{ }
const double &PpiX;
const double &PpiY;
const int &SizeX;
const int &SizeY;
// EDIT: explicit assignment operator was missing
ImageInfo& operator=(const ImageInfo &other)
{
myPpiX = other.myPpiX;
myPpiY = other.myPpiX;
mySizeX = other.mySizeX;
mySizeX = other.mySizeX;
return *this;
}
};
The values are stored in the private variables that can be set at construction, and their values accessed by const references. You're also not dependent on the references passed into the constructor living as long as the ImageInfo instance.
As they are data fields that can be modified, they are not const.
If you want to restrict post-construct access to them to const, you need to wrap them in accessors as follows:
struct ImageInfo
{
ImageInfo(const double &ppix, /*...*/)
: PpiX_(ppix),
/*...*/
{ }
double const& PpiX() const {return PpiX_; };
double const& PpiY() const {return PipY_; };
int const& SizeX() const {return SizeX_; };
int const& SizeY() const {return SizeY_; };
private:
double PpiX_;
double PpiY_;
int SizeX_;
int SizeY_;
};
That allows move/copy assignment and construction, while blocking non-const access outside of said construction.
Avoiding the () is tricky, but could be done with pseudo-references, something like this:
struct pseudo_const_reference_to_Ppix {
ImageInfo const* self;
operator double() const { return self->Ppix; }
void reseat( ImageInfo const* o ) { self = o; }
};
plus a whole pile of boilerplate to overload every const operator on the left and right such that the above pseudo_const_reference_* is just as valid as double.
Generic versions can be written (either taking a functor or a std::function if you are willing to suffer type erasure overhead).
Then you maintain these pseudo-const references on assignment and copy/move construction.
I think the () is the better option.
Note that the overhead of a pointer (or more) per pseudo-reference is basically unavoidable: member variables do not have access to the this from which they are invoked, even though the accesing site has it right there plain as day.
If something is const, you can't change it. Full stop.
So you must adjust the design somewhere, either not have those ImageInfo members const, not have ImageInfo as member, or best: not do the assignment.
Normally const members are set in constructor. You can make a load function that creates a MyImageObject object with all its content, so avoiding to have a half-done thing and load the sate in a second phase.
An alternative is to have the mMyInfo indirectly, say using unique_ptr, then you can replace it with another instance. I would not do that without a really good reason.
Immutable value objects are great. But the variable holding the value object (mMyInfo in MyImageObject) should be (non-constant) a pointer. In other languages (e.g. Java) this is automatically the case, but not in C++, where you need the * operator. Also, there is no need to override/implement the = operator for value objects. To change the image data within the image object, you assign a newly constructed ImageInfo object to the myImageInfo pointer. This way, none of the internal variables of the value object are changed.