简体   繁体   中英

Setters/getters and hierarchical data structures

I know it is considered a bad idea to have public fields in your class. But what's the best approach when your class includes numerous hierarchical data structures and fields? For example:

class A {B d1; C d2; D d3;}
class B {E d4; F d5;}
class E {G d6; int d7;}

In C it is quite easy to access such a data structure, eg ptr_to_A->d1.d4.d7 and so on... But what's the approach when we use setters/getters?

When using setters and getters in C++, expressions like A.get_d1().get_d4().get_d7() don't seem convenient, and they force returning references. Since some structures are pretty big, returning by value seems a terrible idea.

Which approach or coding style do you use in these cases? Maybe getting rid of setters/getters and making these fields public?

In my coding style, a class should not expose public "raw" data members, but only getters and setters (even if they are simple one-line methods).

This is because the code can be upgraded in the future, and the single-line method can be expanded to something more complicated (or some debug-only builds features can be added to check some invariants, etc.), so it's better to keep the interface consistent for the clients (which is not possible if you expose "raw" data members).

You can avoid using the get_() prefix, and just consider the data member as a "property" with a simple (without get_... ) name, eg

class Shape
{
public:
  ....

  COLORREF Color() const // Just Color() i.e. the property name, without get_...
  {
     return m_color;
  }

private:
  COLORREF m_color;
};

And write client code like:

Shape s;
COLORREF someColor = s.Color();

which looks fine to me.

For the setter you can use a syntax like:

Shape& Color(COLORREF color)
{
    m_color = color;
    return *this;
}

and write client code like:

Shape s;
s.Color(...).Draw(); // set color and draw shape

If the type of the property is something more complex than a COLORREF (which is a 32-bit DWORD ), you can use a pattern like:

std::wstring Name() const // getter
{
    return m_name;
}

Shape& Name(std::wstring name) // setter
{
    // Pass by value and move from the value (C++11 move semantics)
    m_name = std::move(name);

    return *this;
}

I know it is considered a bad idea to have public fields in your class.

This is a sweeping statement that has come from last decade's Java development. You should be considering whether a member should be public or private on a member-by-member basis. There are times when a public data member is the right idea. Consider the following questions:

  1. Do I need to maintain an invariant over this member?
  2. Can this member have an invalid value?
  3. Do I want the interface to give an alternate representation of this member?

If the answer to any of the above questions is yes, you probably want to use a getter.

Also consider whether it actually makes sense to set members individually. Perhaps you should be setting members with the constructor, and you want to provide some other interface that modifies those members.

When using setters and getters in C++, expressions like A.get_d1().get_d4().get_d7() don't seem convenient

While it's not too uncommon to have fairly deep nesting of data structures, usually a specific piece of code shouldn't have to delve too far into it. If it does, I imagine it's probably doing more than it should, going beyond its single responsibility. However, if it is a common task to get d7 from an A object, perhaps A should expose it in its interface:

int A::get_d7 {
  return get_d1().get_d4().get_d7();
}

Since some structures are pretty big, returning by value seems a terrible idea.

Actually, with modern C++, this is not a problem at all. Passing by value should be considered your default mode of object passing. This is because temporary objects can now be moved from, which is essentially a very efficient form of copying.

If you are just using a class as a pure data structure, and there is no behavior relating to that data that you want to encapsulate, then use a struct instead and access the fields directly. Bjarne Stroustrup recommends this approach . This is equivalent to using a class and declaring all the members as public , but calling it a struct instead makes it clearer that it is nothing more than simple collection of data.

If you are doing more than just storing data, then use getters and setters.

When using setters and getters in C++, expressions like A.get_d1().get_d4().get_d7() don't seem convenient, and they force returning references. Since some structures are pretty big, returning by value seems a terrible idea.

No, you can choose whether to return by reference or by value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM