Dispelling Common C++ Class Myths

Introduction

In discussions about the C++ programming language, particularly about the differences between struct and class keywords, sometimes I have read or heard the following statements:

  1. A struct is a value type, while a class is a reference type.
  2. A struct contains only primitive data members and is designed for small objects.
  3. A struct cannot inherit from another struct or class, and it cannot be the base of a class.
  4. Unlike classes, structs can be instantiated without using the new operator.

All three are false in C++ and are quoted/borrowed from other programming languages. This article can help C++ beginners to discover the differences between class and struct in the C++ programming language, for further avoiding false assumptions like the ones presented above.

Note: we’ll use upper-case letters for ‘CLASS’ as a concept to distinguish by ‘class‘ keyword.

What are CLASSES?

This is a general definition quoted from Bjarne Stroustrup’s FAQ:

A class is the representation of an idea, a concept, in the code.

By short, as stated in the C++ standard, CLASSES are types.
CLASSES can be defined using one of the following class-keys (keywords):

  • class

    class CFoo
    {
       // CFoo is a CLASS defined by using 'class' keyword
    };
    

  • struct

    struct SFoo
    {
       // SFoo is a CLASS defined by using 'struct' keyword
    };
    

  • union

    union UFoo
    {
       // UFoo is a CLASS defined by using 'union' keyword
    };
    

What is the Difference Between Class and Struct?

The members of a class are private by default, while the members of a struct are public by default (‘default’ means when no ‘private’, ‘protected’, or ‘public’ access specifier is used).

struct SFoo
{
   int m; // SFoo::m is public by default
};

class CFoo
{
   int n; // CFoo::n is private by default
};

int main()
{
   SFoo sfoo;
   CFoo cbar;
   sfoo.m = 1; // OK. SFoo::m is public by default
   cbar.n = 1; // Error: cannot access private member CFoo::n.

   return 0;
}

Also, the default access to a base class is private for class and public for struct.

class CBase
{
public:
   int m;
};

struct SDerived : CBase
{
   //...
};

class CDerived : CBase
{
   // ...
};

int main()
{
   SDerived sd;
   CDerived cd;

   sd.m = 1; // OK
   cd.m = 1; // Error: private access to CBase

   return 0;
}

The next examples show equivalent definitions for SFoo and CFoo.

struct SFoo : CBase // default public access
{
   int m; // SFoo::m is public by default.
};

struct SFoo : public CBase // public access specifier.
{
public:
   int m; // SFoo::m is public because of access specifier.
};

class CFoo : CBase // default private access
{
   int m; // CFoo::m is private by default.
};

class CFoo : private CBase // private access specifier.
{
private:
   int m; // CFoo::m is private because of access specifier.
};

Note: As a good programming practice it is recommended to explicitly use the access specifiers (private, protected, and public) even where they are not explicitly necessary.

Getting Rid of False Assumptions

Now, let’s resume the three statements presented in the introduction:

  1. A struct is a value type, while a class is a reference type.

    This is false in C++. When refering to CLASSES (either defined by using ‘class’ or ‘struct’), concepts like ‘value type’ and ‘reference type’ have no sense. However, we use ‘by value’, ‘by reference’, or ‘a reference to’ when we are talking about objects (instances of CLASSES). The objects of both types (‘struct’ and ‘class’) can be passed either by value or by reference. Also, we can declare references to objects of type struct and class.

    struct SFoo
    {
       //...
    };
    
    class CFoo
    {
       //...
    };
    
    void func1(SFoo foo) {/*...*/}
    void func2(CFoo foo) {/*...*/}
    void func3(SFoo& foo) {/*...*/}
    void func4(CFoo& foo) {/*...*/}
    
    int main()
    {
       SFoo sfoo;
       // pass struct type object by value
       func1(sfoo);
       // pass struct type object by reference
       func3(sfoo);
       // declare a reference to struct type object
       SFoo& rsfoo = sfoo;
    
       CFoo cfoo;
       // pass class type object by value
       func2(cfoo);
       // pass class type object by reference
       func4(cfoo);
       // declare a reference to class type object
       CFoo& rcfoo = cfoo;
       // ...
       return 0;
    }
    

  2. A struct contains only primitive data members and is designed for small objects.

    Not true. A struct can contain everything a class contains: trivial or complex data members, (static, virtual) member functions, special functions like constructors, destructor, overloaded operators, and so on. Also there is no restriction regarding the struct instance size vs. class instance size.

  3. A struct cannot inherit from another struct or class, and it cannot be the base of a class.

    This is also false. In C++, a CLASS defined by using the struct keyword can inherit from another struct or class.

    struct SBase
    {
       //...
    };
    class CBase
    {
       //...
    };
    struct SFoo : public SBase
    {
       //...
    }
    struct SBar : public CBase
    {
       //...
    };
    

    Also it can be the base of another struct or class

    // struct SBase is base class for SFoo and CFoo.
    struct SBase
    {
       //...
    };
    struct SFoo : public SBase
    {
       //...
    };
    class CFoo : public SBase
    {
       //...
    };
    

  4. Unlike classes, structs can be instantiated without using the new operator.

    This one is false as well. In C++, both struct and class types can be instantiated by using the new operator.

    struct SFoo
    {
       //...
    };
    class CFoo
    {
       //...
    };
    
    int main()
    {
       SFoo* pSFoo = new SFoo; // OK. Instantiate a struct.
       CFoo* pCFoo = new CFoo; // OK. Instantiate a class.
       //...
       delete pSFoo;
       delete pCFoo;
    
       return 0;
    }
    

Conclusion

In C++ programming language, there is no difference between CLASSES defined by using struct or class keywords, except the default access to members and base classes.

  • objects of type struct and type class can be passed either by value or by reference;
  • we can declare references to both struct and class type objects;
  • a struct can contain everything a class contains;
  • there is no restriction regarding the struct instance size vs. class instance size;
  • both struct and class can inherit from other structs or a classes;
  • both struct and class can be the base for other structs or a classes;
  • both struct and class can be instantiated by using the new operator.

Resources

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read