The real difference between struct and class

“Should I use a struct or a class?”

Such is the question many C++ programmers ask themselves, or ask around to more experienced co-workers, when designing their code.


There is sometimes a cloud of misconception about what the difference between struct and class technically is, particularly amongst the youngest developers. And once we get to understand the technical difference, some degree of uncertainty often remains about which one to use in a given context. Sometimes developers even disagree about which is the more appropriate in their code.

Let’s start by clearing up the situation, by stating the technical difference between struct and class, and then propose rules to choose between the two, by looking at what the C++ Core Guidelines written by the Jedis of C++ have to say about it.

The legal difference

In terms of language, except one little detail, there is no difference between struct and class. Contrary to what younger developers, or people coming from C believe at first, a struct can have constructors, methods (even virtual ones), public, private and protected members, use inheritance, be templated… just like a class.

The only difference is if you don’t specify the visibility (public, private or protected) of the members, they will be public in the struct and private in the class. And the visibility by default goes just a little further than members: for inheritance if you don’t specify anything then the struct will inherit publicly from its base class:

struct T : Base // same thing as "struct T : public Base"

while the class will do private inheritance:

class T : Base // same thing as "class T : private Base"

That’s it. No other difference.

Once we get past this language precision, the following question arises: if struct and class are so similar, when should I use one or the other?

The real difference between struct and class: what you express by using them

The difference that really matters between struct and class boils down to one thing: convention. There are some conventions out there that are fairly widespread and that follow a certain logic. Following these conventions gives you a way to express your intentions in code when designing a type, because as we’ll see in a moment, implementing it as a struct doesn’t convey the same message as implementing it as a class.


In a word, a struct is a bundle. A struct is several related elements that needed to be tied up together in a certain context. Such a context can be passing a restricted number of arguments to a function:

struct Point
   double x;
   double y;
void distance(Point p1, Point p2);

Although it’s a bundle, struct can be used to effectively raise the level of abstraction in order to improve the code: in the above example, the distance function expects Points rather than doubles. And on the top of this, the struct also has the benefit of logically grouping them together.

Another context is returning several values from a function. Before C++17 and structured bindings, returning a struct containing those values is the most explicit solution. Have a look at Making your functions functional for more about making function interfaces clearer.


In two words, a class can do things. A class has responsibilities. These responsibilities can be quite simple, like retrieving data that the class may even contain itself. For this reason, you want to use the term class when you are modelling a concept (that has an existence in the business domain or not), the concept of a object that can perform actions.

Contrary to a struct, a class is made to offer an interface, that has some degree of separation from its implementation. A class is not just there to store data. In fact a user of a class is not supposed to know what data the class is storing, of if it contains any data at all for that matter. All he cares about is its responsibilities, expressed via its interface.

A class raise the level of abstraction between interface and implementation even more than a struct does.

Sometimes a type that was initially implemented as a struct ends up turning into a class. This happens when you realize the various bits that were bundled turn out to form a higher level concept when they are considered together, or have a stronger relation that what was perceived initially.

This is were invariants come into play. An invariant is a relation between the data members of a class that must hold true for the methods to work correctly. For example, a std::string can hold a char* and a size in its implementation (well at least conceptually, since modern string implementations are more complex than that due to optimizations). Then an invariant is that the number of characters in the allocated char buffer must match the value in the size member. Another invariant is that the char* is initialized and points to valid memory.

Invariants are set into place by the constructor of the class and the methods make the assumption that all the invariants hold true when they are called, and ensure they remain true when they finish. This can be a tacit agreement, or, as has been discussed for standardization, such pre-conditions and post-conditions in methods could one day be explicitly stated in code, and checked at run-time.

Finally a simple rule of thumb for choosing between struct or class is to go for class whenever there is at least one private member in the structure. Indeed, this suggests that there are implementation details that are to be hidden by an interface, which is the purpose of a class.

The C++ Core Guidelines

The above has been inspired by the C++ Core Guideline (which is a great read by the way), in particular the following:

Author: Jonathan Boccara

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.