2.2.  Class Definitions

[ fromfile: classes.xml id: definingclasses ]

C++ has another datatype called class that is similar to struct. A class definition looks like this:

class ClassName {
    public:
         publicMembers 
    private:
         privateMembers 
};

The first line of the class definition is called the classHead.

The features of a class include data members, member functions, and access specifiers (public, private, protected). Member functions are used to initialize, manipulate, or otherwise manage the data members. Chapter 5 discusses functions in more detail, especially those features found in C++ but not in some other languages. For now, we will use functions in ways that should be clear from the context and familiar to you from your earlier experience with other languages.

After you have defined a class, you can use the class name as a type for variables, parameters, and returns from functions. Variables of a class type are called objects, or instances, of the class.

Member functions for class ClassName specify the behavior of all objects of type ClassName. Each member function can access all other members of the class. Nonmember functions can only manipulate objects indirectly by calling member functions.

The set of values of the data members of an object is called the state of the object.

Header Files

To define a class (or any other type) you should place its definition in a header file, preferably with the same name as the class, and with the .h extension. Example 2.3 shows a header file that contains a class definition.

Example 2.3. src/classes/fraction/fraction.h

#ifndef _FRACTION_H_ 
#define _FRACTION_H_  

#include <QString>


class Fraction {
public:
    void set(int numerator, int denominator);
    double toDouble() const;
    QString toString() const;
private:
    int m_Numerator;
    int m_Denominator;
};

#endif


Header files are #included in other files by the preprocessor. To prevent a header file from accidentally being #included more than once in any compiled file, wrap it with #ifndef-#define ... #endif preprocessor macros (Section C.2).

Generally, you should place the definitions of member functions outside the class definition in a separate implementation file with the .cpp extension.

Example 2.4 is an implementation file that contains definitions of the functions declared in Example 2.3.

Example 2.4. src/classes/fraction/fraction.cpp

#include <QString>
#include "fraction.h"


void Fraction::set(int nn, int nd) {
    m_Numerator = nn;
    m_Denominator = nd;
}

double Fraction::toDouble() const {
    return 1.0 * m_Numerator / m_Denominator;
}

QString Fraction::toString() const {
  return QString("%1 / %2").arg(m_Numerator).arg(m_Denominator);
}


Every identifier has a scope (Section 20.2), a region of code in which a name is "known" (or visible) and accessible. In earlier examples, you saw identifiers with block scope, which extended from the line in which the identifier was declared down to the end of the code block that contained that declaration. The identifier was not visible above the declaration or below the end of that block.

Class member names have class scope. To begin with, class scope includes the entire class definition, regardless of where in that class definition the member name was declared. It also extends into the implementation (.cpp) file. The definition of any class member outside the class definition requires a scope resolution operator of the form ClassName:: before its name. The scope resolution operator tells the compiler that the scope of the class extends beyond the class definition and includes the code between the symbol :: and the closing brace of the function definition.

For example, the members Fraction::m_Numerator and Fraction::m_Denominator are visible inside the definitions of Fraction::toString() and Fraction::toDouble() even though they are declared in a separate file.

It is often necessary to display an object, save it to a file, or send it over a network to another program. There are many ways to perform all these operations. The toString() member function typically returns a string containing a "snapshot" of the current state of an object. You can use that string for debugging, display, storage, transmission, or conversion purposes. To increase flexibility, you can give the toString() function one or more parameters that permit the string to have a variety of formats. Many Qt classes have done this; for example, QDate::toString() returns a variety of date formats depending on which Qt::DateFormat is passed in as an argument.

You should make a practice of supplying most of your classes that have data members with a toString() member function. Generally, your class definitions should not have member functions that display or transmit the values of data members (e.g., display(), print(), saveToFile(), readFromFile(), etc.). [22]



[22] General purpose, stream-oriented serialization of data for both input and output is discussed further in Section 7.4.1.