If I need to write a setter and/or getter for I write it like this:
struct X { /*...*/};
class Foo
{
private:
X x_;
public:
void set_x(X value)
{
x_ = value;
}
X get_x()
{
return x_;
}
};
However I have heard that this is the Java style of writing setters and getters and that I should write it in C++ style. Moreover I was told it is inefficient and even incorrect. What does that mean? How can I write the setters and getters in C++?
🛈 There has been a lot of chatter about not needing getters and setters. While I agree with most of what's been said here (the toy example above ideed doesn't need to be a getter/setter), I still advocate for the need to know how to idiomatically write such methods because there are legitimate reasons where getters and setters are the right solution or even unavoidable. And they are ubiquitous, even if they might not look at first glance as a setter or getter but they are, or at least the pattern for writing them applies.
E.g.:
For instance: getting the size of a vector. You don't want to expose a data member, because it publicly needs to be read only. And it's not justified to add complexity by making vector::size
some sort of ReadOnly<size_t>
with friend vector
. It might also be just a delegate to another object's size.
class Vector
{
private:
X* m_buffer;
size_t m_size;
public:
// is this getter needed? Yes
// is it ok? (spoiler: no, it needs const)
size_t get_size() { return m_size; }
};
class Vector
{
private:
Buffer m_buffer;
public:
// is this getter needed? Yes
// is it ok? (spoiler: no, it needs const)
size_t get_size() { return m_buffer.size(); }
};
Getters and setters don't need to just expose a data member. Think about getting and setting an element of an array. There is logic there even if there is a plain dumb index access with no range check. You can't just expose a data member, there is no data member to expose in the first place. It's still a getter/setter pair you can't avoid:
class Vector
{
public:
// are these getters and setters needed? Yes!!
// are they ok and C++ idiomatic? (spoiler: no)
void set_element(std::size_t index, X new_value);
X get_element(std::size_t index);
};
Knowing the C++ idiomatic way of writing getters and setters will allow me to write the above get_element
/set_element
in a C++ idiomatic way.
There are two distinct forms of "properties" that turn up in the standard library, which I will categorise as "Identity oriented" and "Value oriented". Which you choose depends on how the system should interact with Foo
. Neither is "more correct".
Identity oriented
class Foo
{
X x_;
public:
X & x() { return x_; }
const X & x() const { return x_; }
}
Here we return a reference to the underlying X
member, which allows both sides of the call site to observe changes initiated by the other. The X
member is visible to the outside world, presumably because it's identity is important. It may at first glance look like there is only the "get" side of a property, but this is not the case if X
is assignable.
Foo f;
f.x() = X { ... };
Value oriented
class Foo
{
X x_;
public:
X x() const { return x_; }
void x(X x) { x_ = std::move(x); }
}
Here we return a copy of the X
member, and accept a copy to overwrite with. Later changes on either side do not propagate. Presumably we only care about the value of x
in this case.