Chapter 6. Classes

C++ supports object-oriented programming with a traditional, statically-typed, class-based object model. That is, a class defines the behavior and the state of objects that are instances of the class. Classes can inherit behavior from ancestor classes. Virtual functions implement type polymorphism, that is, a variable can be declared as a pointer or reference to a base class, and at runtime it can take on the value of any class derived from that base class. C++ also supports C-style structures and unions using the same mechanism as classes. This chapter describes classes in all their glory. For information about class templates, see Chapter 7. Member functions are a special kind of function; see Chapter 5 for general information about functions.

The syntax descriptions in this chapter are informal. See Chapter 12 for a precise BNF grammar.

A class definition starts with the class , struct , or union keyword. The difference between a class and a struct is the default access level. (See Section 6.5 later in this chapter for details.) A union is like a struct for which the storage of all the data members overlap, so you can use only one data member at a time. (See Section 6.1.3 later in this section for details.)

A class definition can list any number of base classes, some or all of which might be virtual. (See Section 6.4 later in this chapter for information about base classes and virtual base classes.)

In the class definition are declarations for data members (called instance variables or fields in some other languages), member functions (sometimes called methods), and nested types.

A class definition defines a scope, and the class members are declared in the class scope. The class name itself is added to the class scope, so a class cannot have any members, nested types, or enumerators that have the same name as the class. As with any other declaration, the class name is also added to the scope in which the class is declared.

You can declare a name as a class, struct, or union without providing its full definition. This incomplete class declaration lets you use the class name in pointers and references but not in any context in which the full definition is needed. You need a complete class definition when declaring a nonpointer or nonreference object, when using members of the class, and so on. An incomplete class declaration has a number of uses:

Example 6-1 shows several different class definitions.

Some programming languages differentiate between records and classes. Typically, a record is a simple storage container that lacks the more complex features of a class (inheritance, virtual functions, etc.). In C++, classes serve both purposes, but you can do things with simple classes (called POD, for plain old data) that you cannot do with complicated classes.

Basically, a POD class is a structure that is compatible with C. More precisely, a POD class or union does not have any of the following:

Also, all nonstatic data members must have POD type. A POD type is a fundamental type, an enumerated type, a POD class or union, or a pointer to or array of POD types

Unlike C structures, a POD class can have static data members, nonvirtual functions, and nested types (members that do not affect the data layout in an object).

POD classes are often used when compatibility with C is required. In that case, you should avoid using any access specifier labels because they can alter the layout of data members within an object. (A POD class cannot have private or protected members, but you can have multiple public: access specifier labels and still have a class that meets the standard definition of a POD class.)

Example 6-2 shows POD types (point1 and info) and non-POD types (point2 and employee).

The virtue of a POD object is that it is just a contiguous area of storage that stores some value or values. Thus, it can be copied byte for byte and retain its value. In particular, a POD object can be safely copied to an array of char or unsigned char, and when copied back, it retains its original value. A POD object can also be copied by calling memcpy; the copy has the same value as the original.

A local POD object without an initializer is left uninitialized, but a non-POD object is initialized by calling its default constructor. Similarly, a POD type in a new expression is uninitialized, but a new non-POD object is initialized by calling its default constructor. If you supply an empty initializer to a new expression or other expression that constructs a POD object, the POD object is initialized to 0.

A POD class can contain padding between data members, but no padding appears before the first member. Therefore, a pointer to the POD object can be converted (with reinterpret_cast<>) into a pointer to the first element.

A goto statement can safely branch into a block, skipping over declarations of uninitialized POD objects. A goto that skips any other declaration in the block results in undefined behavior. (See Chapter 2 for more information about initializing POD objects, and Chapter 4 for more information about the goto statement.)

A union is like a struct, but with the following restrictions:

An object of union type has enough memory to store the largest member, and all data members share that memory. In other words, a union can have a value in only one data member at a time. It is your responsibility to keep track of which data member is "active."

A union can be declared without a name (an anonymous union), in which case it must have only nonstatic data members (no member functions, no nested types). The members of an anonymous union are effectively added to the scope in which the union is declared. That scope must not declare any identifiers with the same names as the union's members. In this way, an anonymous union reduces the nesting that is needed to get to the union members, as shown in Example 6-3.