Classes

Class Definition

The standard format for a C++ class definition is as follows. Note that definitions are terminated with an ending semi-colon after the closing bracket.

 

class <classname>
{
private:
    // private members go here

protected:
    // protected members go here

public:
    // public members go here
};

 

 

Member Functions

Class objects call member functions, or methods, using the following syntax. In object-oriented programming, we think of calling member functions as “sending a message” to the object.

 

<object_name>.<function_name>( <arguments> );

 

Consider the following example class and application

class rectangle
{
private:
    float width, height;

public:

    // Constructor
    rectangle();

    // Compute and return area
    float area();

    // Access methods
    float getWidth();
    float getHeight();
    void setWidth( float );
    void setHeight( float );
};

// Method definitions
rectangle::rectangle()
{
    width = height = 0.0;
}
float rectangle::area()
{
    return (width * height);
}
float rectangle::getWidth()
{
    return width;
}
float rectangle::getHeight()
{
    return height;
}
void rectangle::setWidth( float w )
{
    width = w;
}
void rectangle::setHeight( float h )
{
    height = h;
}
 

#include <iostream>
using namespace std;

main()
{
    rectangle rect1;
    rect1.setWidth( 10.0 );
    rect1.setHeight( 20.0 );

    rectangle rect2;
    rect2.setWidth( 10.0 );
    rect2.setHeight( 15.0 );

    cout << “Area of rect1: “ << rect1.area() << endl;
    cout << “Area of rect2: “ << rect2.area() << endl;
}

In the application, two objects of type rect are allocated in memory, each storing different values for their state variables. The area function sends a message to each of the objects. Instead of "call the area function", we "tell the rectangle to compute the area.” This reinforces the object-oriented approach. We consider our rectangle objects as independent, self-contained entities that we communicate with to perform various tasks.

 

 

Access Privileges

A C++ class provides three levels of information hiding and protection using the following access keywords.

    private
                - accessible only by other members of the class
                - cannot be accessed by derived classes

    protected
                - level of data hiding between private and public
                - normally used when deriving a new class
                - considered private to users (outside class)
                - accessible by other members of the class
                - accessible by other classes derived from the class

    public
                - accessible by other members and any user of the class
 

The keywords private, protected, public are optional and, by default, all members are private unless specified otherwise. Class members take on the level of security by the keyword preceding them in the class definition. These keywords can be placed in any order and can be included more than once. With regard to these keywords, a good practice when designing classes is as follows:

 

Make all members private that do not need to be accessed outside of the class (hide the implementation details and keep data internal)

Consider the following example class and application.

class AAA
{
 
private:
     
int a;
     
void add();

      public:
        int c;
        void sub();
      };

      // Method definitions
      void AAA::add()
      {
         a += c;
      }
      void AAA::sub()
      {
         add();
      }  

      // Application
      main()
      {
         AAA myObj;
         myObj.add();
         myObj.sub();
      }
 

Will this application compile? Note that the member function add is defined as private. In the application, this member function is inaccessible. The compiler recognizes this fact and reports the following error during compilation.

In function `int main()':

error: `void AAA::add()' is private

error: within this context

 

 

Consider another example.

           

class employee
{
    long soc_sec_no;
    void compute();
public:
    int salary_range();
    char *name;
};

         

main()
{
    employee a;
    strcpy( a.name, "Clark Kent" );
    a.compute();
}

 

Will this application compile? All members are considered private unless otherwise specified in a class. The member function, compute, is private and therefore cannot be accessed by the object in the application. The compiler recognizes this fact and reports the following error during compilation.

In function `int main()':

error: `void employee::compute()' is private

error: within this context

 

Using Class Objects
Objects declared from class definitions are used in a main program in much the same manner as standard declared variables (float, int) and structures in the C/C++ programming language. Let’s look at some of the ways that class objects can be used in applications using the following example class.

#include <iostream>
using namespace std;

// Class definition

class Test

{

private:

  float val;

 

public:

  Test();

  void Init();

  void Print();

  void setVal( float v ) { val = v; }

  float getVal() { return val; }

};

 

//

// Class member definitions

//

// Constructor

Test::Test()

{

  Init();

}

// Initialize state

void Test::Init()

{

  val = 0.0;

}

// Print state

void Test::Print()

{

  cout << “Test object:” << endl;

  cout << “  val: " << val << endl;

}

Class objects can be used in arrays just as other variable types.

main()

{

  Test t[10];

  for( int i = 0; i < 10; ++i )

    {

      t[i].setVal( float(i) );

      t[i].Print();

    }

}

 

Output

Test object:

        val: 0.000000

Test object:

        val: 1.000000

Test object:

        val: 2.000000

Test object:

        val: 3.000000

Test object:

        val: 4.000000

Test object:

        val: 5.000000

Test object:

        val: 6.000000

Test object:

        val: 7.000000

Test object:

        val: 8.000000

Test object:

        val: 9.000000


Class objects can be passed to functions by value and by reference. Note that just as with standard variables, called functions that modify objects passed by value (function F1 below) only change a copy of the object, not the original object in the calling function. Note the state variable from object t1 has not been modified after the call to function F1. Conversely, called functions that modify objects passed by reference (function F2 below) change the original object in the calling function. Examine the example application below and its output carefully.

void F1( Test );

void F2( Test * );

 

main()

{

  Test t1,t2;

 

  t1.setVal( 10.0 );

  t2.setVal( 20.0 );

 

  // Pass by value

  F1( t1 );

  t1.Print();

 

  // Pass by reference

  F2( &t2 );

  t2.Print();

}

 

void F1( Test a )

{

  a.setVal( 15.0 );

}

void F2( Test *a )

{

  a->setVal( 15.0 );

}

 

Output

Test object:

        val: 10.000000

Test object:

        val: 15.000000


Class objects can be returned by functions. The function F3 below returns an object with the same state as the object passed to the function.

Test F3( Test );

 

main()

{

  Test t1,t2;

 

  t1.setVal( 10.0 );

  t2 = F3( t1 );

  t2.Print();

}

 

Test F3( Test a )

{

  Test b;

  b.setVal( a.getVal() );

  return b;

}

 

Output

Test object:

        val: 10.000000

Pointers can be used to access class objects.

main()

{

  Test t1, *pT;

 

  // Assign address of t1

  pT = &t1;

 

  // Modify state of t1

  t1.setVal( 10.0 );

 

  // Dereference pointer to print state of t1

  pT->Print();

}

 

Output

Test object:

        val: 10.000000

Classes can be nested. An object of type Test is nested as a state variable in the class Test2 below. In this way, classes can store other class objects as part of its internal information (or state variables).

class Test2

{

private:

  Test t;                       // Nested Test object

 

public:

  Test2() { t.setVal(10.0); }

  void Print();

};

void Test2::Print()

{

  t.Print();

}

 

main()

{

  Test2 t;

  t.Print();

}

Output          

Test object:

        val: 10.000000

Memory can be allocated dynamically for class objects. In the example below, we are dynamically allocating a single object on the heap. Note that rather than using malloc (as we did in the C language), we use a different method using the keyword new in C++. We will be examining this topic in-depth in later notes.

main()

{

  Test *t = new Test();

  t->setVal( 10.0 );

  t->Print();

}

 

Output          

Test object:

        val: 10.000000

 

 

Additional Features
In addition to the standard uses above, class objects have additional features provided by C++. Some of these new object-oriented features include the following:

    - Facility for information hiding
    - Ability to have functions as members
    - Facility for conveniently initializing and cleaning up objects
    - Facility to support inheritance with derived classes and virtual functions
 

 

C++ Structures
Since C is a subset of C++, we can still use the struct type in C++. A struct in C++ is defined simply to be a variant of a class where all the members default to public (all members in a class default to private). In C++, structures are a bit more powerful than those in C. C++ structures can be defined to have the same features as a class in C++. Specifically, in C++ the struct type can include functions, can inherit features (as we’ll later), and can use information hiding with access keywords described above. In other words, a C++ structure defined as follows

struct rectangle
{
    int width, height;
};

is equivalent to the C++ class:

class rectangle
{
public:
    int width, height;
};

Note finally, that in C++ we do not need to use typedef to rename our structure. We can use the name directly in an application as in the following example.

struct rectangle
{
    int width, height;
};

main()
{
    rectangle rect;
    ...
}

 

Designing Methods
Class designers need to think about the ways users might interface or use class objects. Success of a class depends on the completeness and efficiency of the design. It's often helpful to think of the general tasks any well-designed class should encapsulate. These tasks are implemented as various types of class methods (or member functions). Let's look at four general ways of categorizing the tasks that every well-designed class should support. Consider this as a type of checklist for any future class you might design. We’ll be encountering many of these types of functions during our study of C++ details.

    Manager functions

- Manage class objects
- Initialization
- Assignment
- Memory management
- Copy functions
- Print functions
- Type conversion
- Usually invoked implicitly by the compiler


    Implementor functions

- Provides the capabilities associated with the class
- Describes and implements the essential behavior of the class
- Provide capabilities for operations with other objects


    Helping functions

- Carry out auxiliary tasks
- Usually not intended to be invoked directly by the user
- Provide support for other class member functions
- Generally declared as private


    Access functions

- Member functions supporting user access to private data
- Allows user to get and set private data within bounds of class design