Class Definitions And Declarations

Introduction

  • Classes let us define our own types that are customized for the problems we need to solve, resulting in applications that are easier to write and understand.
  • A class defines data and function members.
  • Class types often are referred to as abstract data types. An abstract data type treats the data (state) and operations on that state as a single unit.

Class Definitions and Declarations

Class Members

  • Each class defines zero or more members.
  • Members can be either data, functions, or type definitions.
  • A class may contain multiple public, private, and protected sections.

Constructors

  • A constructor is a special member function that has the same name as the class.
  • Its purpose is to ensure that each data member is set to sensible initial values.
  • Constructors should always have the same name as the class.
  • Constructors have no return type.

Example: Class that holds a fractional value as an integer numerator and denominator.

class Fraction
{
private:
    int m_nNumerator;
    int m_nDenominator;
 
public:
    Fraction() // default constructor
    {
         m_nNumerator = 0;
         m_nDenominator = 1;
    }
 
    int GetNumerator() { return m_nNumerator; }
    int GetDenominator() { return m_nDenominator; }
    double GetFraction() { return static_cast<double>(m_nNumerator) / m_nDenominator; }
};

Example: Above example rewritten with a default constructor.

#include <cassert>
class Fraction
{
private:
    int m_nNumerator;
    int m_nDenominator;
 
public:
    // Default constructor
    Fraction(int nNumerator=0, int nDenominator=1)
    {
        assert(nDenominator != 0);
        m_nNumerator = nNumerator;
        m_nDenominator = nDenominator;
    }
 
    int GetNumerator() { return m_nNumerator; }
    int GetDenominator() { return m_nDenominator; }
    double GetFraction() { return static_cast<double>(m_nNumerator) / m_nDenominator; }
};

Destructors

  • Executed when an object of that class is destroyed.

Specific naming rules:
1) The destructor must have the same name as the class, preceded by a tilde (~).
2) The destructor can not take arguments.
3) The destructor has no return type.

Example

class MyString
{
private:
    char *m_pchString;
    int m_nLength;
 
public:
    MyString(const char *pchString="")
    {
        // Find the length of the string
        // Plus one character for a terminator
        m_nLength = strlen(pchString) + 1;
 
        // Allocate a buffer equal to this length
        m_pchString = new char[m_nLength];
 
        // Copy the parameter into our internal buffer
        strncpy(m_pchString, pchString, m_nLength);
 
        // Make sure the string is terminated
        m_pchString[m_nLength-1] = '\0';
    }
 
    ~MyString() // destructor
    {
        // We need to deallocate our buffer
        delete[] m_pchString;
 
        // Set m_pchString to null just in case
        m_pchString = 0;
    }
 
    char* GetString() { return m_pchString; }
    int GetLength() { return m_nLength; }
};
 
int main()
{
    MyString cMyName("Alex");
    std::cout << "My name is: " << cMyName.GetString() << std::endl;
    return 0;
} // cMyName destructor called here!

Member Functions

Member functions must be declared, and optionally may be defined, inside the class; functions defined inside the class are inline.
Member functions defined outside the class must indicate that they are in the scope of the class.

Example
The definition of Sales_item::avg_price uses the scope operator to indicate that the definition is for the avg_price function of the Sales_item class.

double Sales_item::avg_price() const
{
    if (units_sold)
        return revenue/units_sold;
    else
        return 0;
}

Example
Member functions may be declared const by putting the const keyword following the parameter list:
A const member may not change the data members of the object on which it operates. The const must appear in both the declaration and definition. It is a compile-time error for the const to be indicated on one but not the other.

double avg_price() const;

Data Abstraction and Encapsulation

The fundamental ideas behind classes are data abstraction and encapsulation.

Data abstraction is a programming (and design) technique that relies on the separation of interface and implementation. The class designer must worry about how a class is implemented, but programmers that use the class need not know about these details. Instead, programmers who use a type need to know only the type's interface; they can think abstractly about what the type does rather than concretely about how the type works.

Encapsulation is a term that describes the technique of combining lower-level elements to form a new, higher-level entity. A function is one form of encapsulation: The detailed actions performed by the function are encapsulated in the larger entity that is the function itself. Encapsulated elements hide the details of their implementation—we may call a function but have no access to the statements that it executes. In the same way, a class is an encapsulated entity: It represents an aggregation of several members, and most (well-designed) class types hide the members that implement the type.

Access Labels

  • public: Members defined after a public label are accessible to all parts of the program. The data-abstraction view of a type is defined by its public members.
  • private: Members defined after a private label are not accessible to code that uses the class. The private sections encapsulate (e.g., hide) the implementation from code that uses

Class Declarations versus Definitions

A class is completely defined once the closing curly brace appears. Once the class is defined, all the class members are known. The size required to store an object of the class is known as well. A class may be defined only once in a given source file. When a class is defined in multiple files, the definition in each file must be identical.

It is possible to declare a class without defining it. This declaration, is referred to as a forward declaration.
A class must be fully defined before objects of that type are created. The class must be defined—and not just declared—so that the compiler can know how much storage to reserve for an object of that class type. Similarly, the class must be defined before a reference or pointer is used to access a member of the type.

class Screen; // declaration of the Screen class

Forward Declaration

Class Design Examples

Class Mechanics: The following class has poor style and some errors. Can you find the errors and fix the design of the class?

#include <iostream>
 
using namespace std;
class Complex
{
    public:
        Complex(double real, double imaginary = 0)
            : _real(real), _imaginary(imaginary)
        {}
 
        void operator+ (Complex other)
        {
            _real = _real + other._real;
            _imaginary = _imaginary + other._imaginary;
        }
        void operator<<(ostream os)
        {
            os << "(" << _real << "," << _imaginary << ")";
        }
        Complex operator++()
        {
            ++_real;
            return *this;
        }
        Complex operator++(int)
        {
            Complex temp = *this;
            ++_real;
            return temp;
        }
 
    private:
        double _real, _imaginary;
};
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-Share Alike 2.5 License.