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



About the Author

Ovidiu Cucu

Graduated at "Gh. Asachi" Technical University - Iasi, Romania. Programming in C++ using Microsoft technologies since 1995. Microsoft MVP awardee since 2006. Moderator and article reviewer at Codeguru.com, the number one developer site. Co-founder of Codexpert.ro, a website dedicated to Romanian C++ developers.

Related Articles

Comments

  • Point 4 clarification pls

    Posted by Wilbur on 04/30/2012 04:34pm

    Yes, but can structs be initiated WITHOUT 'new' as stated in point 4?

    Reply
  • Useful

    Posted by Promotional Engine on 03/19/2011 10:46am

    Thanks... Then the only difference is: "default access to members and base classes" : struct:public; class:private;

    • RE: Useful

      Posted by ovidiucucu on 03/19/2011 11:18am

      Right!

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds