Next: , Previous: Exception handling, Up: Top


4 Using Classes

4.1 Initializing the class

Before you can use your class, you must initialize it! Initializing a class will automatically initialize its superclasses, so if you have inherited your class from occ system classes (like RefCounted), you do not have to initialize them separately. However, because ooc uses the Exception class internally, you must initialize it before calling any ooc function.

     
     int
     main( int argc, char * argv[] )
     {
         ooc_init_class( Exception );
         ooc_init_class( MyClass );
     
         do_my_program();
     
         return 0;
     }
     

4.2 Creating an object of a class

Creating an object is easy with the ooc_new marco, or with the equivalent ooc_new_classptr function.


The ooc_new( classname, void * param ) macro converts the Class name to the appropriate class description table address, and calls the ooc_new_classptr function. Use ooc_new when you create your class from a statically known class (you know the class name). The second parameter is passed to the class constructor code without any modification or check. You can parameterize your constructor this way, it is advisable passing a variable or struct pointer here. The variable or the struct must exist until the constructor returns!

The ooc_new_classptr( Class class_ptr, void * param ) function creates an object of the class pointed by the first parameter. Use this function when you know only the class description table's address! This is very rare situation, and I guess it is mainly useful inside the ooc code. The second parameter is passed to the class constructor code without any modification or check.

The above methods return objects of Object type.

Although using the ooc_new macro for object creation is easy, it is advisable to define a parameterized ..._new() function for each class, because that way you can control the parameter checking, as well as the automatic conversion of return type (ooc_new returns Object that you must cast to the desired type).
     
     String str_new( char * initial )
     {
         return ooc_new( String, initial );
     }
     

4.3 Deleting an object

The created objects must be deleted with one of the deletion functions. They must not be freed with the standard memory handling functions, like free or ooc_free!

4.3.1 Deleting an object directly

Deleting an object can be done with the ooc_delete( Object ) function. It calls the class destructors on the object, and frees the allocated memory. Any pointer to this object will not be usable after deleting the object! Use this way an object destruction when you would like to destroy objects that you did allocate temporarily in your C functions as local variables.

4.3.2 Deleting object via pointer

In many cases it is important to mark that the object has been deleted by nulling the pointer. But this operation rises some issues regarding circular object references, destroying non-complete ojects and multi-threading.


For your convenience there is the object deletion function via its pointer, ooc_delete_and_null( Object *), which does exactly the same in a thread-safe (or at least reentrant) way.
     
     void String_destructor( String self, StringVtable vtab )
     {
         /* Deletes the member object and nulls the pointer */
         ooc_delete_and_null( & self->other );
     }
     

Always use this way a object destruction when you would like to destroy objects that you did allocate globally or as members of other classes! Especially it is important in class destructor codes! (Since ooc 1.0, it is guaranteed that the destructor runs only once. However it is still recommended nulling pointers in your code when deleting, just for clarity.)

4.4 Accessing class members

The class members can be accessed via the object pointer if they are made public, although it is not an advisable method. It is recommended accessing the class members only via the class methods.

4.5 Finalizing a class

A class can be finalized when no longer needed, although it is not necessarily required. If your class has reserved some global resources, then you can release them in the class' finalize code. One may neglect finalizing, if known, that the class did not allocate any global resources. However it is a good practice to finalize the classes that are not needed in the future, especially in dinamically loadable modules. You can finalize all initialized classes:

     ooc_finalize_all( );

as the last executed line in your code. In case of ooc_finalize_all() the class finalization is done in the reverse order of initialization. However ooc_finalize_all() is the preferred way, you can finalize a given class, like:

     ooc_finalize_class( String );

But be very carefull, here! If you finalize a class that would be required for using or finalizing an other class then your code will crash! ooc_finalize_all() keeps track of class dependencies, so this problem does not exist that case.

4.6 Dynamic type checking

ooc provides dynamic type safety for your objects, because the object types are known in run time. You can safely cast Objects using the ooc_cast macro. If the cast fails then err_bad_cast exception is thrown. An example of using the dynamic cast in ooc:

     DeclareClass( String, Base );
     DeclareClass( Utf8, String );
     DeclareClass( Something, Base );
     
     Something something = something_new();
     Utf8   my_utf8_string = utf8_new( "This is an utf8 string." );
     String my_string;
     
     my_string = ooc_cast( my_utf8_string, String ); /* Correct */
     my_string = ooc_cast( something, String );      /* Can not be cast,
                                                        exception is thrown */

You can also retrieve the type of an Object. The ooc_get_type function returns the type of an object in run time (actually returns a pointer to the class description table). If the parameter is not a valid Object then err_bad_cast exception is thrown. An example of retriving of the object type in ooc:

     String my_string = string_new( "Test string." );
     
     printf( "The type of my_string is: %s\n", ooc_get_type( my_string )->name );

This example prints The type of my_string is: String on the display.


You can use this function for comparing object types, like
     if( ooc_get_type( my_object ) == & StringClass )
         ... process my_object as a String object here
     else if ( ooc_get_type( my_object ) == & SomeOtherClass )
             ... process my_object as a SomeOther object here

Please note the Class suffix to the object's typename. This pointer to the static class description table is defined for each class.


But there is a more convenient way for dynamic type checking in ooc, that handles the class inheritance correctly. The ooc_isInstanceOf macro returns TRUE if the object is an instance of a given class or of any of its superclasses, FALSE otherwise. The typical use of this kind type checking is at the very beginning of the class methods, to make sure that the parameter object is of the right type. Because this is a bit computationally "expensive" operation, it is usually placed into an assert macro, that lets you some control distinguishing debug and release versions.
     void
     string_example_method( String self )
     {
         assert( ooc_isInstanceOf( self, String ) );
     
         /* Do your method here!
            You can be sure, that self is a valid
            instance of String class.
            (At least in the debug version!)
         */
     }