Refining Object Behavior: Part II

Controlling Vertical Access

By: LT Rick Miller, USN

In my last article I showed you how to control horizontal access between objects by using a combination of access specifiers and friend functions. By controlling horizontal access between objects you can limit the level of access one class of objects has to another. (Figure 1). You can further refine object behavior by defining vertical access between classes.

What is Vertical Access?

Vertical access is that level of access granted between classes in a class inheritance hierarchy. Through the use of access specifiers, a base class defines what functions and attributes will be horizontally accessible by other objects as well as what functions and attributes will be inherited by derived classes. Additionally, by using access specifiers in an inheritance specification, derived classes can further define which functions and attributes inherited from the base class will be made available to other derived classes farther down the inheritance chain.

Access Specifiers

Levels of access can be defined within a class by using one of three access specifiers: public:, protected:, and private:. Listing 1 declares a class called Base_Class and uses all three access specifiers to control access to various class attributes and functions.

	1  class Base_Class
	2  {
	3    public:
	4      Base_Class(int ival = 10);
	5      ~Base_Class();
	6      void Set_i(int ival);
	7      int Get_i();
	8    protected:
	9      int j;
	10   private:
	11      int i;
	12  };
Listing 1. Base_Class class declaration

Horizontal access to Base_Class objects is limited to the public functions listed under the public: access specifier on line 3. The integer attribute i, appearing under the private: specifier on line 10, is private to Base_Class objects and must be accessed through the public interface functions Set_i() and Get_i(). The protected: access specifier on line 8 grants the same horizontal access level to the integer attribute j as private: does to integer attribute i. In other words, the access specifiers protected: and private: grant the same level of horizontal access. Figure 2 illustrates the horizontal access protection granted by using the protected: and private: access specifiers.

Using Access Specifiers in Inheritance Specifications

The difference between the protected: and private: access specifiers comes into play when you use class inheritance. Figure 3 shows a class called Derived_Class inheriting the functionality of Base_Class.

Any member functions and attributes declared to be public or protected in Base_Class will be inherited by Derived_Class. Referring to Listing 1, all the public interface functions as well as the protected attribute j will be inherited by Derived_Class. Derived_Class can then specify what horizontal access to grant the inherited members, while at the same time setting their level of export control from Derived_Class to any class that may inherit from Derived_Class in the future. Listing 2 gives the Derived_Class declaration specifying public inheritance.

	1  class Derived_Class : public Base_Class
	2  {
	3     public: 
	4       Derived_Class(int ival = 10);
	5       ~Derived_Class();
	6  };

Listing 2. class Derived_Class using public inheritance

Public Inheritance

Derived_Class uses the public inheritance specifier on line 1 of Listing 2 above. By using public inheritance, Derived_Class will inherit the public interface functions and the protected attribute j from Base_Class while keeping horizontal access specification the same as it was in Base_Class. Figure 4 illustrates how members of Base_Class are brought down into Derived_Class using public inheritance.

Derived_Class will provide two services by using public inheritance. First, it will continue to make those functions that were declared public in Base_Class available to client objects while preventing access to inherited protected members. Second, it makes the public and protected members of Base_Class available for inheritance to any future subclass of Derived_Class. This behavior makes public inheritance the preferred form of inheritance.

Protected Inheritance

Sometimes, however, you will want your derived classes to behave somewhat differently from their base classes.

	1  class Derived_Class : protected Base_Class
	2  {
	3    public: 
	4      Derived_Class(int ival = 10);
	5      ~Derived_Class();
	6  };

Listing 3. class Derived_Class using protected inheritance

Listing 3 shows the Derived_Class declaration using the protected inheritance specifier. Figure 5 illustrates how protected inheritance affects the public and private members of Base_Class.

Protected inheritance will cause the public interface functions of Base_Class to be brought down and hidden in Derived_Class's protected section. This effectively blocks horizontal access to Base_Class interface functions through Derived_Class objects while allowing them to be inherited as protected members by any class that may inherit from Derived_Class in the future.

Private inheritance

Private inheritance offers further inheritance control by making all members inherited from a base class private to the derived class. Listing 4 gives the Derived_Class declaration using private inheritance. Figure 6 illustrates how Base_Class's public and protected members are applied to Derived_Class.

	1  class Derived_Class : private Base_Class
	2  {
	3    public: 
	4      Derived_Class(int ival = 10);
	5      ~Derived_Class();
	6  };

Listing 4. class Derived_Class using private inheritance

Private inheritance essentially stops the inheritance chain. While other classes can inherit directly from Base_Class in order to acquire Base_Class functionality, they will not be able to gain the same measure of functionality by inheriting from Derived_Class. Any functionality passed along to derived classes of Derived_Class will have to be defined in either Derived_Class's public: or protected: section.

Summary

Object behavior can be further refined vertically by using public, protected or private inheritance specifiers. Public inheritance preserves the original horizontal access specification of inherited base class public and protected members while enabling further inheritance of base class functionality. Protected inheritance makes all inherited base class members protected to the derived class and limits future inheritance options. Private inheritance makes all inherited base class members private to the derived class which effectively prevents their further inheritance.

References

Margaret A. Ellis, Bjarne Stroustrup. The Annotated C++ Reference Manual. Addison- Wesley Publishing Company, Reading, Massachusetts, 1990.

About the author: LT Rick Miller, USN