Programming Language Concepts Using C and C++/Object Orientation

Success of a business depends on its competitiveness, which generally translates to offering easily-upgradeable, high quality products at a moderate price in a reasonable time. This is not any different in software industry: the faster, the less costly, the more flexible the better. Key to achieving this is avoidance of doing the same thing more than once: reuse.

Reusing code, for instance, will save us the development time of the reused module; it will generally lead to faster and/or smaller code because it has been profiled and fine tuned; it will be more reliable because it has been debugged.

In a procedural programming language such as C or Pascal, where subprogram is the natural unit of reuse, composition is the way to achieve reuse: One builds complex subprograms by composing it from simpler ones. Object-oriented programming languages offer an alternative: inheritance. In addition to composing objects from sub-objects, one can use inheritance to derive a new class from an existing one, which means objects of the new class will contain a sub-object of the base one. In other words, composition and inheritance basically boil down to the same thing. The difference lies in the agent that carries out the process: programmer and compiler, respectively.

In this chapter, a very simplistic simulation of object-orientation&mdash;with hopes of giving an insight&mdash;is provided in C. While perusing through the code keep in mind that this should not be taken as an authoritative source. There is certainly much more to it than is offered in this code.

Additionally, static checker, Splint, is introduced. It can be utilized to check source code for semantic errors, such as memory leaks, infinite loops, use of uninitialized data, and so on. Such a tool and the compiler can be used in tandem to make C a safer programming language.

Superclass
Header files ending with the _Private suffix contain information that normally should not have been revealed to the user of a module. This apparent mistake is due to the compilation model of C [and C++], which dictates that in order to define a variable a compiler must have access to the definition of relevant types. That doesn’t hurt much if you use pointers to structure instead of plain structures. However, inheritance requires composing objects from sub-objects, which means the derived type needs to have access to the structure of the base type. Now, it is very likely that implementers of the base type and the derived type are different [groups of] programmers. There must be a way to transmit the required information between these parties. The only way this can be done in C is through placing the type definitions in a header file, which is what we do in the files whose names end with the _Private suffix.

Employee_Private.h

Next structure defines the members of an  object. Among these are two pointers-to-function, which can be made to point to different locations in the code segment. In other words different  objects can have different values in these pointers, which would mean they can react differently to the same requests. In our case, we will say that all s get a salary and a bonus, calculation method of which depend on whether a particular   is in fact an   or a.

Note that we include details of the structure in the header file, which is against our usual practice of deferring to the implementation file. This could still be done in such a manner by turning the structure definition into a pointer to an incomplete type. However, it would not have reflected the true nature of inheritance: inheritance is composition carried out by the compiler; an object of the subclass containsmdash;in addition to its own data members&mdash;a sub-object of the superclass.

Employee.h

C, in the name of improving performance, is a very permissive programming language. This usually means most of the bookkeeping stuff that is deferred to compilers in many other programming languages fall to the share of the programmer. That’s okay when the program is not big and/or the programmer is a competent one. She can take the initiative and skip some of the aforementioned stuff, which would generally mean faster and leaner code. Or ...

One way to deal with the ominous alternative is to use a tool that detects programming anomalies in the source code. One such tool is Splint, which statically checks C programs for potential security vulnerabilities and programming mistakes. Doing this may sometimes require annotating the source code. These annotations are stylized comments that start with  and end with. Following prototypes are examples to this.

before the return type indicates that the function may return&mdash;in addition to a pointer to some memory region&mdash;a  value. Such a possibility means a careless programmer may end up trying to use non-existent  objects. Something we would not like to see happening and our friend, Splint, will not let it happen. The programmer must either make sure the returned value is not  or put up with the complaints of Splint. For different ways of assuring Splint that the returned pointer will never be, take a look at here.

The preceding paragraph is actually a rephrase of the "declare before use" rule we stated in the Sample C Programs chapter. The compiler is contented with declaration of identifiers and [in the case of external entities when the corresponding definition cannot be found in the current preprocessed file] defers the control for the existence of a corresponding definition to the linker, which will not let you use an undefined identifier. In other words, compiler-linker duo is responsible for ensuring undefined identifiers are not used. However, since the heap region is managed by the programmer&mdash;in other words, objects are dynamically allocated [defined] by the programmer&mdash;it is not possible for the compiler-linker duo, which complete their job before run-time and are therefore static in nature, to enforce this rule for objects allocated in the heap. Programmer must walk the extra mile! In a programming language such as Java, this translates to guarding against the relevant exception, whereas in C it means a check against.

A complementary annotation is, which states that related identifier cannot have   as its value. For instance, destruction can take place only when it is applied on an existing object, which implies the argument passed to the destructor function must be non-. Using this as an assurance, Splint will not let you pass some variable that can possibly have  as its value.

As for the  annotation, it is used to relax definition checking for the relevant declaration. Storage annotated as such is assumed to be defined when it is used; no error is reported if it is not defined before it is returned or passed as a parameter.

Definition: An object [that is, a memory region] is said to be defined if storage required for it has been allocated. An object is completely defined if all storage that may be reached from it is defined.




 * }

In our example, construction of an -derived object&mdash;  or  &mdash;is completed in two different constructor functions. After allocating memory in either one of  or , control is passed to   to initialize certain fields. Initialization is later completed in the caller of. This means there will be uninitialized fields on entry to, which further implies there may be undefined fields.

The  annotation is used to denote a parameter that corresponds to an input argument and therefore must be completely defined. In our case, successfully constructing an -derived object guarantees its complete definition.

Note, unless otherwise specified, Splint assumes all unannotated references&mdash;storage reachable from global variables, arguments, and return values&mdash;to be completely defined. One can relax this requirement by turning  flag off.

Employee.c An  object can exist only as a sub-object of another object. This containing object is passed as the first argument to our constructor-like function. If this argument value is, which means there is no containing object, we return without creating an   object. After all the notion of an  is an abstract one and cannot be concretized. However, the concept of an  is a concrete one and we can therefore create its object, which in turn contains an   part. The following constructor-like function serves to make allocations and initializations for this part.

Following  statement is here to convince Splint about our awareness of the possibility of a   return value from. Thanks to this control we will not be harassed by Splint’s annoying warnings.

In case you may be sure the result will never be &mdash;or you simply don’t care&mdash;and you don’t want to write this code fragment you may turn this control off by annotating every use of   with.

If the value returned by the following statement is assigned to a global variable the underlying memory region will be shared between this [global] variable and the actual parameter.




 * }

Considering the programmer may well forget this and go on to free it through the argument, Splint steps in to give us a warning. Not concerned about it, we remove the check by means of the  flag.

Remember the object being destroyed may be anything that derives from. That is, if needed, we can extend the definition of  and come up with a new type, such as. How we extend the base type is however unknown to the base type itself. For this reason, our destructor-like function frees only those regions common to all s.

Returning the department an  works in is no different than returning the department of a. Same can be said about returning her name. Regardless of which type of  object we deal with&mdash;be that an ,  , or even a type that is yet to be defined&mdash;the way the answer is provided will always be the same. Therefore, instead of duplicating this unchanging behavior in all modules, it’ll be wise to put such functionality in a central repository, which in our case happens to be the base type,.

Thanks to this property of constant behavior&mdash;unlike salary and bonus calculation functions&mdash;these functions need not be invoked through pointers-to function, which implies binding of a call to either one of these functions can be performed before run-time.

Definition: A function call bound before run-time is said to be statically dispatched. For such a function, one can find out which function definition will be executed upon making the function call by simply reading the source code.

Subclasses
Engineer_Private.h

An  object is made up of two parts: its   sub-object and   sub-object.

Engineer.h Engineer.c

Note the following allocation reserves memory big enough to hold the  and   sub-objects. Next thing we do, after memory allocation, is calling the constructor-like function of the base type,. Realize we do this before we initialize the -specific part of the object. The logic behind this is we may make use of certain information from the  part initialization for completing the  -part initialization. However, it never happens the other way around: you don’t make use of -part information to complete the  -part initialization.

Now that  is a concrete type we must provide implementations for salary and bonus calculations.

Note that the sole parameter in the function header is not used in the function body. A rather peculiar situation! Not surprisingly, Splint agrees with us on that and reports the potential problem source. Not caring a bit about the advice in this occasion, we get rid of the annoying warning by lifting the particular requirement. This is done by the  flag.

Manager_Private.h Manager.h

Next annotation marks the type declaration as that of a Boolean type. Identifiers defined to belong to this type can only be used&mdash;unless otherwise specified with some other annotation&mdash;in a Boolean context.

Manager.c

Indulging ourselves in some genuine C programming, we find ourselves using a Boolean value as an integer. That is not against the rules of C but Splint sees it as a potential bug and issues a warning. We should either take some corrective action, if there is any mistake, or relax the rules of Splint. We take the second path and state this using the  annotation.

Test Program
Inheritance_Test.c

Following annotation changes the special character that is used to mark the start and end of the stylized comments. From this point on, annotations will start with  and end with.

calculates the total payment [that is, bonus plus base salary] made to the s listed in the array passed to it. Each component of the array can be either an  or a. We cannot be sure of which component will belong to which group. But we can be certain about one thing: regardless of its real type, each component can be treated as an. However, this has a limitation of its own: an  can answer questions that can be asked to all objects of the derived types. And indeed, that’s what we do: get the name of the, calculate her salary and her bonus. We do not, for instance, inquire her discipline. Neither do we ask whether she accepts bribes or not. Because, the former is special to s while the latter is special to  s.

Next function call will be resolved before run-time&mdash;at link-time, to be exact&mdash;while the following two will be resolved at run-time. This is due to the fact that pointer values stored in  and   are determined at the time of object creation, which happens during run-time.

Definition: Resolution of a function’s address at run-time is called dynamic dispatch. The ability of the same call resolving to different functions is called polymorphism.

Since  returns a possibly   value, which is later passed to , Splint raises an objection: How can you be so sure about printing the contents of a possibly non-existing character string? Well, I cannot but in this context it doesn’t seem very likely. So, I relax null-checking and let this one slip.

Note we cast each /  to an. This will allow us to treat them the same way. Well, almost! The list of functions invoked using these objects will be limited to those that can be invoked using an. However, the function invoked will depend on the type of the underlying object.

The  type here [that is, the type of the pointer variable] is called the static type&mdash;because it can be determined by reading the source code&mdash;whereas the type of the underlying object [that is, the type of the region pointed to by the pointer] is referred to as the dynamic type.

C++ equivalent of the next line is given below, which reflects the compiler's behind-the-scenes effort. Thanks to the inheritance relationship between  and , which is relayed to the compiler by means of the ':' operator, the cast is now implicit. Similarly,  argument meaning an   object&mdash;not an object of a class inheriting from the   class&mdash;is being created is not needed anymore. Finally, pointer values needed in the process of dynamic dispatch are gathered by the compiler&mdash;upon seeing the  keyword before the function signature&mdash;and inserted into a structure called vtable, which in turn is pointed to by an implicit field in the object being created, as a result of executing the compiler-synthesized code fragment.



Notice the different ways of convincing Splint about our awareness of the probable  value returned from the constructor function. All of these guarantee that a  pointer will never be dereferenced. In the first technique, we accomplish this by making an assertion that returned pointer is not null, which means the program will terminate upon receiving a  from. Second and third techniques basically boil down to the same thing: by means of an -statement the goal is achieved by explicitly checking the returned value against.

Running the Test Program

 * splint -ID:/include Employee.c↵   # In Cygwin
 * Splint 3.1.1 --- 02 May 2003


 * Finished checking --- no warnings


 * splint -ID:/include Engineer.c↵
 * Splint 3.1.1 --- 02 May 2003


 * Finished checking --- no warnings


 * splint -ID:/include Manager.c↵
 * Splint 3.1.1 --- 02 May 2003


 * Finished checking --- no warnings


 * splint -ID:/include Inheritance_Test.c↵
 * Splint 3.1.1 --- 02 May 2003


 * Finished checking --- no warnings


 * gcc -ID:/include –o InheritanceTest.exe Inheritance_Test.c Employee.c Engineer.c Manager.c↵
 * Inheritance_Test↵


 * NAME         SALARY       BONUS         TOTAL
 * Eng 1		1000        300           1300
 * Mng 1		1500        3000          4500
 * Mng 2		1500        1000          2500
 * Eng 2		1000        300           1300

Total payment: 9600