In C++, when should I use a class vs a struct?

In C++, the only technical difference between a struct and a class is that, by default (i.e., if you don’t specify anything), members of struct are public while members of a class are private. You can achieve encapsulation in a C++ struct by setting up a private section, and putting your data members and your internal member functions into that section. Likewise, a C++ struct can also have protected members in a protected section.

So, technically speaking, you can achieve the very same things with struct or class…only the default access is different. This near equivalence was implemented in C++ because it was easy to implement it in the language. The default public access for struct members was done for backward compatibility with C, so that a C struct would just compile and everything would be automatically public, just as it always is in C. The fact that you can have private members, protected members, and member functions in a struct, well, that’s just thrown in for free. Keep in mind that, just because something is free, doesn’t mean it’s worth using.

Order of the Access Specifiers

I never make use of default access in either a class or a struct, because I want the source code to explicitly state the access for all members. I don’t want there to be any ambiguity for the human reader of the code. So, in a class, I will have an explicit public section, followed by a protected section (if any), and followed by a private section. Likewise, if I define a new struct in C++, I will have an explicit public section, protected section (if any), and private section (if any).  I use that specific ordering of the sections for a reason. I always place public members first, since those will be read by the widest audience — the users of the data type. I place protected members second, because those will be read by a much smaller audience — folks who derive from the data type. I place private members last, because no one but the maintainers need to read about them, and they are subject to change over time anyway.

It’s also best not to have more than one of the same access specifier in a class definition. Have just one public section, one protected section (if any), and one private section. This approach is much easier for the reader/maintainer of the code to deal with.

If you can effectively achieve the same thing with either a struct and class, and you are always explicit with public, protected, and private sections, which one should you use when?

Well, the language doesn’t enforce this, but nearly every coding standard and organization I’ve worked with uses classes for objects and structs for plain old data (POD). In other words, when you don’t need a full-fledged object, but you just need to lay out a chunk of data, use struct. Otherwise, use class. This is an expectation many professional developers have, so if they’re reading the code and see struct, they’ll immediately think “plain old data.” If they see a class, they’ll immediately think “full-fledged object.” You want to follow the principle of least astonishment. Don’t surprise the reader by using a struct for a full-fledged object or a class for plain old data. It will slow the reader down, and thus increase the cost of maintaining the code.

For example, let’s say you need to deal with a file format that has a well-defined file header area with many fields in it. All you need to do is read the header into memory and access the fields in the header. There may be no need for construction, destruction, member functions, etc. You just need access to the data, by name, at the correct offsets, overlaid on the buffer you read in. In this case, you would use a struct, because you’re dealing with plain old data. The same might be true for a well-defined communication packet header.  In other words, you would use a struct in C++ the same way you would use a struct in C.

Bottom line: Use class for a full-blown object. Use struct for plain old data.

You wouldn’t expect some part of the post to appear below the bottom line, but there is one overriding factor to consider. If your project or team or local coding standard uses different criteria to choose between struct and class in C++, you should follow whatever conventions they use. It is far more important to be consistent across an entire project than to follow an industry norm. It’s great if the two are in sync, but they aren’t always in sync in the real world.

Leave a Reply