Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use getters and setters without generating a copy?

I would like to know how to use getters and setters for a member variable which takes a lot of memory. Usualy i would do as bellow:

class A
{
private:
  BigObject object;
public:
  BigObject getObject() const
  {
    return object;
  }

  void setObject(const BigObject& object)
  {
    this->object = object;
  }
};

However this getter and setter i believe will copy the BigObject which i do not want. Is there a better way to do this?

I thought of doing it this way but i read on the internet that it's not a good idea because it can lead to a segmentation fault if used badly:

BigObject& getObject()
{
  return object
}
like image 465
ThatGuyAgain Avatar asked Aug 15 '19 19:08

ThatGuyAgain


2 Answers

(If you do not care about encapsulation in this case, meaning the A::object member should be modifiable by anyone without restriction, then look at SergeyA's answer).

Return by const reference in order to avoid copying and still maintain encapsulation (meaning the caller can't modify the member by mistake):

const BigObject& getObject() const
{
    return object;
}

If the caller actually wants a copy, they can do so easily themselves.

If you want to prevent dangling references (the segfault you mentioned) when the getter is used on a temporary, you can only return a copy when the getter is actually called on a temporary:

BigObject getObject() const &&
{
    return object;
}

const BigObject& getObject() const &
{
    return object;
}

This will return a copy when calling getObject() on a temporary. Alternatively, you can completely prevent calling the getter on a temporary by deleting that particular overload:

BigObject getObject() const && = delete;

const BigObject& getObject() const &
{
    return object;
}

Keep in mind that this is not a guaranteed safety net. It prevents some mistakes, but not all of them. The caller of the function should still be aware about object lifetimes.

You can also improve your setter, btw. Right now, it will always copy the object regardless how the caller passes the argument. You should take it by value instead and move it into the member:

void setObject(BigObject object)
{
    this->object = std::move(object);
}

This requires that BigObject is movable though. If it's not, then this will be even worse than before.

like image 78
Nikos C. Avatar answered Sep 21 '22 22:09

Nikos C.


Best solution: make code of your class exactly that:

struct A
{
  BigObject object;
};

Explanation - avoid trivial setters and getters. If you find yourself putting those into your classes, expose the member directly and be done with it.

Do not ever listen to people who'd say "But what if in the future we add non-trivial logic"? I have seen more than a healthy dose of trivial setters and getters, been around for decades, and never replaced with something non-trivial.

like image 35
SergeyA Avatar answered Sep 21 '22 22:09

SergeyA