The difference between struct and class in C++

Sunday, 21 February 2010

I've seen a lot of people asking about the differences between the use of the struct and class keywords in C++ lately. I don't know whether there's an influx of C++ programmers due to the upcoming C++0x standard, or whether I've just noticed people asking questions that haven't caught my eye before. Whatever the reason, I'm writing this blog entry as something I can point to the next time someone asks the question.

Declaring and defining user-defined types

The primary use of both the struct and class keywords is to define a user-defined type. In C++, such a user-defined type is termed a "class" regardless of which keyword is used in the definition. The choice of keyword is in one sense arbitrary, since the same features and facilities are available whichever keyword is used — there is only one semantic difference which we shall look at shortly. The following two class definitions are thus equivalent in all respects apart from the names of the classes:

struct type_a
{
private:
    int data;
public:
    type_a(int data_):
        data(data_)
    {}
    virtual void foo()=0;
    virtual ~type_a()
    {}
};

class type_b
{
private:
    int data;
public:
    type_b(int data_):
        data(data_)
    {}
    virtual void foo()=0;
    virtual ~type_b()
    {}
};

As this little example shows, you can have constructors, destructors, member functions, private members and even virtual member functions in a class declared with the struct keyword, just as you can with a class declared using the class keyword. Though this example doesn't show it, you can also use the struct keyword to declare classes with base classes.

You can even forward-declare your class using one keyword and then define it with the other, though compilers have been known to complain about this usage:

struct foo;
class foo {};

class bar;
struct bar {};

So, what of the minor semantic difference then? The change is in the default access specifier for members and base classes. Though classes defined using either keyword can have public, private and protected base classes and members, the default choice for classes defined using class is private, whilst for those defined using struct the default is public. This is primarily for backwards compatibility with C: the members of a C structure can be freely accessed by all code so in order to allow existing C code to compile unchanged as C++ the default access specifier for members of a class declared with struct must be public. On the other hand, private data is a key aspect of the encapsulation aspect of object-oriented design, so this is the default for those classes declare with class.

C doesn't have inheritance, but the default access specifier for base classes varies with the keyword used to declare the derived class too. It is public for classes declared with struct and private for those declared with class just the same as for data members. You can still override it with an explicit specifier in both cases.

Let's take a quick look at some examples to see how that works:

struct s1
{
    int a; // public
private:
    int b; // private
protected:
    int c; // protected
public:
    int d; // public again
};

class c1
{
    int a; // private
private:
    int b; // still private
protected:
    int c; // protected
public:
    int d; // public
};

struct s2:
    s1, // public
    private c1, // private
    type_b, // public again
    protected foo, // protected
    public bar // public again
{};

class c2:
    s1, // private
    private c1, // still private
    type_b, // private again
    protected foo, // protected
    public bar // public
{};

As far as declaring and defining user-defined types in C++, that is the only difference; in all other respects, classes declared with struct are identical to those declared with class.

C Compatibility

We touched on this a bit earlier: classes declared with the struct keyword can be compiled as C if they don't use any features that are C++ specific. Thus the following is both a valid C++ class and a valid C structure:

struct c_compatible
{
    int i;
    char c;
    double d;
};

It is therefore common to see struct used in header files that are shared between C and C++. Since non-virtual member functions don't affect the class layout you can even have member functions in such a type, provided they are hidden from the C compiler with a suitable #ifdef:

struct baz
{
    int i;

#ifdef __cplusplus
    void foo();
#endif;
};

Templates

There is one place where you can use the class keyword but not the struct one, and that is in the declaration of a template. Template type parameters must be declared using either the class or typename keyword; struct is not allowed. The choice of class or typename in this case is again arbitrary — the semantics are identical. The choice of keyword does not impose any semantic meaning, any type (whether a built in type like int or a user-defined type like a class or enumeration) can be used when instantiating the template in either case.You can of course declare a class template with the struct keyword, in which case the default access for the members of the template is public.

template<class T> // OK
void f1(T t);

template<typename T> // OK
void f2(T t);

template<struct T> // ERROR, struct not allowed here
void f3(T t);

template<class T>
struct S
{
    T x; // public member
};

That's all folks!

These are the only concrete distinctions between the uses of the struct keyword and the class keyword in C++. People also use them for documentation purposes, reserving struct for C-compatible classes, or classes with no member functions, or classes with no private data, or whatever their coding standard says. However, this is just documentation and convention rather than an inherent difference: you could use struct for all your classes, or class for all your classes except those that are shared with C.

Posted by Anthony Williams
[/ cplusplus /] permanent link

| Stumble It! stumbleupon logo | Submit to Reddit reddit logo | Submit to DZone dzone logo

Comment on this post

If you liked this post, why not subscribe to the RSS feed RSS feed or Follow me on Twitter? You can also subscribe to this blog by email using the form on the right.

9 Comments

Another difference is you can initialize a struct's members using a curly-braced and comma-separated array style. AFAIK this won't be available for other types until C++0x. E.g. (don't know if this will be formatted correctly after submission, but here goes):

struct A { int x; int y; }; class B { int x; int y; };

int main() { A a = { 5, 20 }; // This is fine B b = { 6, 21 }; // Compiler outputs "error: scalar object b requires one element in initializer" }
by Michel Boto at 17:45:44 on Saturday, 20 February 2010

@Michael: if you make the members of B public, it will compile. So this is not a real difference.

by saurabh at 02:38:58 on Sunday, 21 February 2010

The default inheritance access specifier is also affected.

by James at 03:10:31 on Sunday, 21 February 2010

@saurabh: true. I'm an idiot.

by Michel Boto at 12:25:47 on Sunday, 21 February 2010

@James: You're right, the default access specifier for inheritance is difference. I did cover that: look at the examples s2 and c2. The preceding text could probably be clearer though: "and base classes" isn't as explanatory as it could be. I'll update the wording.

by Anthony Williams at 21:14:44 on Sunday, 21 February 2010

Bit manupulated variables can be declared only in struct

example -------- struct a { char i : 1; char c : 1; };

by Ram Macharaj at 12:01:00 on Tuesday, 23 February 2010

Hi Ram,

Unfortunately you are mistaken. You can use bitfields in classes declared with "class" as well as those declared with "struct".

by Anthony Williams at 13:30:34 on Tuesday, 23 February 2010

Nice write-up (not to mention a nice captcha too!)

Does the committee intend to allow 'struct' to be used in the same context as 'typename' / 'class' any time in the foreseeable future (not a big deal, just consistent I guess)?

by Anand at 17:40:02 on Tuesday, 31 July 2012

Another difference for templates, specifically for "template template" parameters, where you must use "class" and cannot use "struct" instead:

template< typename T > struct Holder { T x; }; // a struct

template< typename T, template< typename X > struct H = Holder > struct Bad { H<T> h; }; /* --> error: "expected 'class' before 'H'" (with g++) */

template< typename T, template< typename X > class H = Holder > struct Good { H<T> h; }; /* --> OK (even though H might be a struct (and Holder is)) */

int main() { Good< int > g = { { 42 } }; /* g.h.x == 42 */ }

Side note: the following is obviously an error too, you cannot use "typename" instead of "class" here:

template< typename T, template< typename X > typename H = Holder > struct Wrong { H<T> h; };
by Will at 11:42:54 on Sunday, 12 August 2012

Add your comment

Your name:

Your URL:

Email address:

Person or spambot?

Your comment: