RSS

C++ classes and structures: what's the difference?

The text is also available in:
View count: 414

Do you know the dif­fe­ren­ce be­tween a C++ class and a struc­ture? It makes quite a good job in­ter­view ques­tion, as it seems to be hard and tricky, while the actual res­ponse is easy. If you know it, you should also pro­bably guess why my C++ code is com­posed of classes de­fined mostly with the struct key­word in­stead of class. If not, you will find the ex­pla­nation be­low.

Source: pixabay.com

The C langu­age supports two com­plex types, de­fined with union and struct key­words. Complex types are used to re­pre­sent mul­tiple va­lues of diffe­rent types sto­red in a cer­tain me­mo­ry area, with a single variable or a pointer. Of the two, struc­tures (struct), with their mem­ber fields laid out sequen­tially in me­mo­ry, are more use­ful in typi­cal appli­ca­tion code and can be used to group diffe­rent por­tions of in­for­ma­tion so that to­gether they de­scribe com­plex ob­jects. Data struc­tures, along with sub­routines that pro­cess their con­tent, are the basic building blocks for object-orien­ted pro­gram­ming tech­niques.

The C++ langu­age has been de­signed with a high level of C langu­age-com­pa­ti­bi­li­ty in mind. Every C pro­gram is a va­lid C++ one, while ob­vio­usly not using any of C++'s new fun­ctio­na­li­ties. Thus, C++'s struct key­word still de­fines a da­ta struc­ture in the C langu­age fashion. At the sa­me time, struc­tures have been en­han­ced so that they can con­tain methods, sub­routines that are tightly coupled with da­ta and can pro­cess it. Because exter­nal code, called a class user, should never di­rect­ly access an object's fields and should read and up­date the object's state through its methods, new key­words have been in­tro­du­ced that let a prog­ram­mer speci­fy which ele­ments of a da­ta struc­ture should be acces­sible on­ly to its methods (pri­vate) or methods of types in­he­rited from it (pro­tec­ted). However, to pre­vent this me­cha­nism from breaking the com­pa­ti­bi­li­ty with the C langu­age, all struc­ture mem­bers are pub­li­cly acces­sible by de­fault (pub­lic).

To pro­mote field en­cap­su­la­tion among pro­gram­mers and pro­tec­ted them from acci­den­tally making all fields and methods of their da­ta struc­tures pub­lic, a new class key­word has been also in­tro­du­ced. It may not be ob­vious, how­ever, that types de­fined with it are effecti­vely equi­valent to those de­fined with struct and they both get trans­la­ted to the sa­me ma­chi­ne code. The on­ly actual dif­fe­ren­ce lies in the in­ter­pre­tation of de­fault vi­si­bi­li­ty of da­ta struc­ture's mem­bers:

As both the key­words can be used mostly in­ter­chan­ge­ably, pro­gram­mers are taught to li­mit their use of the struct key­word to “plain” da­ta struc­tures that should actu­ally make their con­tent pub­lic, and use class in all other cases of “full-fledged” classes. It is in­deed a sound ad­vice.

However, the slight dif­fe­ren­ce be­tween classes de­fined with struct and class causes an issue. It has been men­tio­ned above that code using ob­jects of a cer­tain class should never be able to di­rect­ly access their fields, and even some of their inter­nal methods. One can achieve such re­sults most easily by pla­cing field decla­ra­tions at the be­gin­ning of the decla­ration of the class de­fined with the class key­word, thus making use of its de­fault pri­vate vi­si­bi­li­ty:

class Circle {
   double r;
public:
   Circle(const double r) : r(r) {}
   double radius() const noexcept {
      return r;
   }
};

It is a good pro­gram­ming prac­tice, how­ever, to hide pri­vate ele­ments of a class not on­ly from code that makes use of the class, but also from a prog­ram­mer who may in­spect its code. The first things he or she should see when looking at the code should be the pub­lic ele­ments of the class. Then, the pro­tec­ted ele­ments can follow, which can be of in­te­rest to pro­gram­mers wil­ling to extend the class through in­heri­tance. Eventually, at the very end of the class defi­nition, there may be the pri­vate ele­ments.

A prob­lem arises when imple­men­ting this rule in a class de­fined with the class key­word. The Circle class pre­sen­ted above would take the follo­wing form:

class Circle {
public:
   Circle(const double r) : r(r) {}
   double radius() const noexcept {
      return r;
   }
private:
   double r;
};

The defi­nition starts with the pub­lic access spe­ci­fier which reverses the de­fault pri­vate vi­si­bi­li­ty. So, in­stead of making use of the pro­per­ties of the class key­word, we are actively fighting it so that the class defi­nition starts with pub­lic ele­ments and not pri­vate ones.

However, we can choose a diffe­rent so­lu­tion. Remember that classes de­fined with the struct key­word are virtually iden­tical to those using the class key­word but for the de­fault access spe­ci­fier. So, the Circle class can be de­fined as follows:

struct Circle {
   Circle(const double r) : r(r) {}
   double radius() const noexcept {
      return r;
   }
private:
   double r;
};

This way, we can get the sa­me result with shorter and more coherent code. However, it is more im­por­tant that we make use of the pro­per­ties of key­words with­out the need to actively chan­ge their be­ha­viour.

Now you can pro­bably see why I pre­fer to intro­duce my classes with the struct key­word. And while I feel a lit­tle bad about igno­ring the new class key­word that should de­note full-fledged classes, I still pre­fer my code to be less expli­cit, and making use of langu­age con­ven­tions, with de­fi­ni­tions of classes star­ting with the ele­ments most im­por­tant to pro­gram­mers who should use them, and ending with the im­ple­men­tation de­tails which should be of lit­tle in­te­rest to any­one but me.