What is a class ?
In the strict sense, an OOP class is a collection of related data with methods for working with this data. However, C ++ is far from being a pure OOP language, and encapsulation is a much better goal, so that some kind of religious, over-senseless utopia.
What data should be placed in a class ?
The associated data should usually be related to each other, for example, the length of the buffer and the buffer associated with it belong to each other, since you cannot use a buffer without its length.
Encapsulation (and the Law of Demeter) will then report that they must be private so that class clients are not dependent on its internal representation. In practice, however, it is viable that some information should be open to the public directly in order to avoid creating many getters / seters functions (although they have their advantages).
At least, although the data that is associated with invariants (*) must be private so that these invariants can be respected.
What functions should be placed in a class ?
Any function that needs direct access to the private data of a class must be a class method. A notable exception are operators whose signature is corrected. This can be solved by giving them friendly access.
Corruption lies in the fact that any function that does not need direct access (since other methods provide sufficient information for its functionality) is better implemented as a free function. This increases encapsulation.
A typical example: std::string::find_first_of (and related) should be implemented as a free function.
What is a blob?
Sometimes you find that you have a bunch of related data, but without a strong invariant between each other, for example:
struct State { Language language; Page currentPage; };
If you change the language, it does not change the page and vice versa. These two are just for convenience. This does not correspond to the class and is sometimes called blob due to its unstructured form. It no longer requires more encapsulation, and the methods are thus superfluous (although the constructor may help).
(*) On invariants
As explained, the length of the buffer is closely related to the buffer itself, they must be changed together. Therefore, there must be a buffer class that has two members: length and buffer, and which manages these two, so that the invariant that length is the length of buffer is always satisfied (with respect to external observers).
On the other hand, if you create an attribute std::string filename; and you hide it in BigObject , along with logmessage and logmessage , then you are doing it wrong. BigObject must have the Filename filename; attribute Filename filename; and allow it to the Filename class to maintain this invariant.