The Three Dominant Modern OOPLs


Certainly, we need to pay more detailed attention to the common object-oriented programming languages of our day. These are C++, Java, and C#, listed in order of their creation.

C++

C++ was created between 1983 and 1985 by Bjarne Stroustrup of Bell Labs. It was approved as an ANSI standard programming language in 1989 and has enjoyed widespread usage since the mid 80s.

Reusable Code

C++ has an extensive amount of low-level code prewritten in its standard library, but it mostly is geared toward collections and low-level data structures such as stacks and queues. Common programming tasks , such as working with databases, images, and Internet connections, are not provided by the C++ language itself but rather by add-on libraries or the operating system.

Performance

C++ is based on C, which is known as a high-performance language. Because of the many similarities, you will often see the two mentioned together, such as referring to a C/C++ topic, as we will be doing here.

C/C++ gains its high-performance advantage at the cost of ease of use. Although C/C++ lets you directly manipulate memory within the level of security provided by the operating system, doing so incorrectly is a common problem. C/C++ lets programmers directly work with any portion of memory they want, and it often requires programmers to manage their own resources. As you ll see, this fact leads to one of the most common misunderstandings about the C++ language.

Most programmers learn C before C++. Because C++ is based on C, this is a well-regarded approach. A typical topic in both C and C++ is pointers, which permit programmers to control dynamically allocated memory, work directly with any area of memory, and other tasks. When going from C to C++, most students continue to apply the C paradigm to this topic rather than the C++ approach.

For example, consider the following example in C, which is a function that loads a file into memory:

 char* Load( const char* Filename ) 
{
char * pRet;
FILE * File;
int Size;
File = fopen( Filename, "r" );
if( File == NULL )
return( NULL ); // Can't open file error
fseek( File, 0, SEEK_END );
Size = ftell( File ) + 1;
fseek( File, 0, SEEK_BEG );
pRet = malloc( Size * sizeof(char) );
if( pRet == NULL )
{
fclose( File );
return( NULL ); // Out of memory error
}
if( fread( pRet, sizeof(char), Size, File ) != Size )
{
fclose( File );
free( pRet );
return( NULL ); // File read error
}
fclose( File );
return( pRet );
}

The Load function works properly, but look at the code and how it returns a pointer that it doesn t free. This is by design, and whoever calls this function must remember to free the memory. The misunderstanding is that many new C++ programmers still continue to write code like this. Instead, they should adapt the C++ approach by wrapping the code up into a class. Here s an example:

 class TextFile 
{
protected:
char* m_pText;
public:
TextFile() // Constructor
{
m_pText = NULL;
}
~TextFile() // Destructor
{
free( m_pText );
}
const char* GetText() // Accessor function
{
return( m_pText );
}
bool Load( const char* Filename )
{
char * pTmp;
FILE * File;
int Size;
File = fopen( Filename, "r" );
if( File == NULL )
return( false ); // Can't open file error
fseek( File, 0, SEEK_END );
Size = ftell( File ) + 1;
fseek( File, 0, SEEK_BEG );
pTmp = malloc( Size * sizeof(char) );
if( pTmp == NULL )
{
fclose( File );
return( false ); // Out of memory error
}
if( fread( pTmp, sizeof(char), Size, File ) != Size )
{
fclose( File );
free( pTmp );
return( false ); // File read error
}
free( m_pText );
fclose( File );
return( true );
}
};

Note that in this example we could have used C++ streams to do the file operation, but we wanted to keep the code looking as similar as possible to the C example. The real demonstration here is that now the user of this class needn t be worried about releasing the memory allocated by the Load function.

Of course, not worrying isn t completely true. There are always exceptions to the rules, where common sense is the only true rule. What we think we have is a completely safe class that wraps and protects the m_pText pointer. Look, we even have an accessor function called GetText . But, consider the following problematic sample usage:

 TextFile F; 
F.Load( Datafile );
free( F.GetText() );

Note that here we are accidentally (and incorrectly) freeing the return value of the GetText , which is the m_pText data member.

Therefore, in this case C++ doesn t make it impossible to shoot yourself in the foot , only harder.

Performance: Compiled Code

C++ is a compiled language. This means that when you write the source code, you must compile and link it to get an executable. This executable is now in machine code for the specific computer on which it was compiled (although cross-compilers permit you to compile programs for other computers, such as building a Palm handheld program on a Windows PC).

Compiled code has a performance benefit over its counterpart , interpreted code. Compiled code, however, must be recompiled to be executed on another platform.

Security

C++ does not provide any sort of security. Any C++ program has the ability to access any block of memory or any resource it wants to (although operating systems such as Windows and UNIX have security to try and stop rogue programs from doing anything inappropriate).

Portability

Because it is an ANSI standard, C++ is the same language on all platforms. However, because C++ is a compiled language, you must compile an application before using it on a new platform. Because the user interface varies among platforms, this is one of the major issues in porting a program from one operating system to another.

Garbage Collection

C++ does not support garbage collection, the ability of a language to automatically clean up resources (memory, specifically ) itself. In C++, if you allocate a block of memory, you are responsible for freeing it as well. The benefit to this feature is that destructors in C++ are true destructors. The downside is that it s possible through a programming error that a destructor is not called at all (which can result in resource leaks).

User Interface

The C++ language does not provide any user interface elements other than buffer console input and output. This means that it has no native support for graphical user interfaces such as Windows, Mac OS X, and the XWindow system. Instead, all these environments have their own application programming interface (API) that permits C++ to be used with them.

Multiple Inheritance

C++ provides support for multiple inheritance, which means that one class can have several simultaneous base classes (as opposed to multiple parents).

Templates

C++ provides a feature called templates . A template can be thought of as the mother of all macros. Templates are used like cookie cutters to define classes and functions that are used to create new classes and functions.

Templates are created using the template keyword and a format, which permits you to define parameters in the creation of the class or function. These parameters are more like empty holes in the definition that are filled when you actually create or use one of the objects or methods .

Here s an example of a template class definition:

 template< class RealType > 
class DataFile
{
protected:
RealType* m_pData;
int m_Size;
public:
RealType GetAt( int Index )
{
if( Index < 0 Index > m_Size )
throw "Bad index";
return( m_pData[ Index ] );
}
DataFile()
{
m_Size = 0;
m_pData = 0;
}
~DataFile()
{
free( m_pData );
}
bool Load( const char* Filename )
{
RealType * pTmp;
FILE * File;
File = fopen( Filename, "rb" );
if( File == NULL )
return( false ); // Can't open file error
fseek( File, 0, SEEK_END );
m_Size = ftell( File ) / sizeof(RealType);
fseek( File, 0, SEEK_SET );
pTmp = (RealType*)malloc( m_Size * sizeof(RealType) );
if( pTmp == NULL )
{
fclose( File );
return( false ); // Out of memory error
}
if( m_Size != (int)fread( pTmp, sizeof(RealType),
m_Size, File ) )
{
fclose( File );
free( pTmp );
return( false ); // File read error
}
free( m_pData );
m_pData = pTmp;
fclose( File );
return( true );
}
};

In this example, the first line contains the template keyword, which contains one parameter: RealType . When we use the template class, we must define the value to be used wherever RealType appears within the template.

We declare a template object as follows :

 DataFile<char> chFile; 

Note that the class name is still DataFile , but we include the < and > brackets to define char as the value for RealType . Therefore, when the compiler sees this, it creates a brand-new class from DataFile and char (unless it has already created it). This is different if a class already exists, in which case you create an instance of the class. A template tells Java to create a new class unless the class already exists. This process means that the following code fragment from the original DataFile would go from

 template< class RealType > 
class DataFile
{
protected:
RealType* m_pData;
int m_Size;

to this:

 class DataFile 
{
protected:
char* m_pData;
int m_Size;
class Person : public IDObject, public Instructor
{
};

Java

Java was initially started in 1991 by James Gosling and others, with the intent of creating a smart environment for devices such as cable boxes. In 1995, however, its direction was changed to work with computers of all sizes. This change was due to the popularity of the Internet, and the diversity of the machines on which it ran.

Initially, Java adopted a write once, run anywhere approach, with the hopes of creating an environment that would enable programmers to create a program once and then run it on any type of device. In order to do this, the Java system was designed on several key components : the Java language, the Java Virtual Machine (JVM), and the Java API.

The Java language is, of course, the language itself and its given syntax. The Java Virtual Machine is a program running on a computer that interprets the tokenized source code of a Java program and executes it. Java is an interpreted, not compiled, language. The Java API is a rich set of classes that provides support for a wide range of features and capabilities in an application.

Java ran into two main problems, however: First, the initial Java Virtual Machines were not completely compatible between the various platforms. Typically, large applications would have parts of a program that would work okay on one platform, but not another, and vice versa. These initial incompatibilities meant that larger, robust systems would not be as reliable and functional as if a traditional programming language had been used.

The second issue Java ran into was that the write once, run anywhere philosophy was blown out of proportion. In reality, you can t create a typical desktop application and then expect it to work on a handheld phone computer because of the physical aspects: Desktops have much more memory, storage, and screen real estate than a typical mobile phone does. Although lower classes could be created for immediate use, designing systems for servers, desktops, and devices proved to be a tremendously different task and approach. To address this, Sun eventually released three different versions of Java, as listed in Table 12-1.

Table 12-1: Editions of Java

Edition

Geared Toward

J2EE Enterprise

Server or enterprise computing

J2SE Standard

Desktop PCs

J2ME Micro

Devices such as handheld PDAs and phones

Reusable Code

Java has an extensive collection of reusable classes. Common programming tasks such as working with databases, images, and Internet connections are built into Java, as are controls and user interface elements such as buttons and check boxes.

Performance: Interpreted Code

Because Java is interpreted, its performance is typically slower than that of a compiled language such as C++. The Java Virtual Machine interprets the tokenized bytecode of a Java program and executes it. The Java Virtual Machine is, in essence, a program that runs a program.

The benefit of the JVM however, is that the same tokenized code can run on different platforms without having to be recompiled, as long as that platform has a JVM.

Security

Java introduced a security model that stops programs from doing explicitly bad or dangerous things. Because programs run under the JVM, they can only do what the JVM permits them to do. Because the JVM does not permit a program to directly access memory, a Java program cannot do so.

The security model used by Java programs falls into two main categories: applets and applications. Applets are small Java programs specifically designed to be hosted in a web browser such as Internet Explorer or Netscape Navigator. Applets have a very strict security model and are prohibited from performing tasks such as creating connections to systems other than from the system that originated the web page. An applet can connect only to systems that generated the web page that contains the applet.

Applications implement security on a sandbox principle. This means that on a given computer, you can configure the various security attributes for an application and what sort of operations it can perform.

Because of its bytecode nature, however, security in Java is more open than in C++ in one particular aspect: Java programs are easier to decompile (the process of going from tokenized code back to source code). Although the source code generated by popular decompilation programs is hard to read, to say the least, it is much less complicated to read than a program that generates a compiled application (such as a C++ program). For companies wishing to protect their software from hacking or copyright infringement, this is not welcome news.

Portability

Portability has been one of the major strengths of Java. The ability to run the same program on a Macintosh as on a Windows PC makes it very attractive. The initial compatibility problems have been worked out (for some time), and this remains a very positive and robust feature of the language.

In order to run a program on a different operating system, you merely need the appropriate JVM installed on the computer. The problem is that there are several versions (not to be confused with editions ) of Java (version 1.5 will probably be released by the time you read this). A Java 1.4 program will not work on a computer that only has Java 1.2 installed. Many companies that distribute Java applications will install their own Java Runtime Environment (or JRE) to ensure the program will operate correctly.

As described earlier in Table 12-1, Java is available for servers, desktops, and handheld devices.

One of the disadvantages of Java is that there is no recognized standard. Whereas C++ and C# both have achieved some sort of recognized standard by a noncommercial committee, Java has not. Sun remains the owner and director of Java, and they decide how and when to change it. Sun recognizes that making drastic changes at this point in time would be a serious error, but the change from 1.0 to 1.1 was a major change that left many programmers wary of future changes. Furthermore, many companies such as HP and IBM are also greatly interested in furthering Java, and they have made their own non-Sun-approved additions (the SWT found in the open-source Eclipse project is a notable item).

Garbage Collection

Java implements garbage collection, meaning that you don t have to worry about releasing the memory you have allocated. If you create an object in Java, you are given a reference to it. If that reference goes out of scope, Java will automatically release any memory that the object used.

Consider the following C++ and Java examples:

 // C++: 
void Foo()
{
DataFile* pFile = new DataFile;
pFile->Open( "log.dat" );
}
// Java
public void Foo()
{
DataFile File = new DataFile
File.Open( "log.dat" );
}

In the C++ function, a DataFile object is dynamically created, and the new operator returns a pointer. We then use the pFile pointer to open a file and then just leave the function. Because C++ doesn t do garbage collection, the object allocated in Foo (and all the data it may contain) is lost because we don t do anything else with the pointer and we don t explicitly delete the object. The C++ Foo function, therefore, has a memory leak.

In the Java function, we are doing something similar. A new DataFile object is dynamically created, but we now get a reference to the actual object. As in the C++ example, we open a file and then leave the function. In Java, however, the JVM will see that the File variable (the only reference to the actual object) has gone out of scope and is no longer available. When the last reference to an object goes out of scope, the JVM marks the object as available for garbage collection, and it will be removed from memory when possible. The removal of the object is an implicit operation.

Some issues arise from garbage collection, however, particularly in the Java implementation:

Collection Times Are Unpredictable

Objects are marked for collection when their last reference goes out of scope, but this does not mean these objects are actually removed from memory at that time. Java implements a garbage collector that runs in a separate thread of execution along with your program, periodically checking for things to clean up. There is no guarantee if or when the collection will run.

Because this collection occurs when the collector gets around to it, it is possible that a bug-free program could repeatedly create a number of objects marked for collection but not actually removed from memory. Because the objects may not have been removed from memory yet, the program could receive an out-of-memory error when creating a new object, even though it released the last reference for all the objects it had previously created.

Java does permit you to force garbage collection as needed, but this introduces a certain amount of overhead.

Java Doesn t Implement True Destructors

Java defines a near-equivalent to a C++ destructor with its finalize method in a class. However, because garbage collection doesn t guarantee that the objects will actually be released, it is possible that the finalize method will never get called.

For C++ programmers, this means that the destructors they rely on are not so reliable in the Java language. Therefore, a more manual process, when compared to C++, is required to perform your own close or shutdown operations on an object when you are done using it in Java.

User Interface

Java provides a rich set of user interface classes for creating applications. The original set of classes was called the Abstract Windowing Toolkit, but it has been replaced for the most part by the Swing classes.

Using these classes, you can create Java applications with buttons, lists, check boxes, and other user interface elements. Although Java lacks an official standard, these classes have remained fairly compatible since the release of the 1.1 version of the Java Development Kit (JDK).

Swing also provides control over the appearance of the user interface components. This is to say that the same button running on the same computer can have several different appearances in a Java, based on the selected look and feel.

Multiple Inheritance

Java does not support multiple inheritance. Many people argue that Java provides interfaces, which are similar, but the reality is that interfaces do not provide for reusable code. Interfaces can mimic multiple inheritance, but the programmer must still write the code for the interface in each class that specifies and implements it.

The issue with multiple inheritance revolves around whether the benefits it provides in terms of reuse offset the complexity of the diamond problem it creates. The complexity revolves around the confusion in a scenario where you have two classes derived from the same base class used as the multiple base classes of some new class. In this scenario, you would have two of the same ultimate base classes in your new class.

Generics/Templates

Although the current version of Java (1.4) does not support templates, as C++ does, templates will be a part of version 1.5, which may be available by the time you read this. In Java version 1.5, the feature is called generics instead of templates, but the result is the same: You can create generic-type-safe classes that the compiler builds for you as needed.

Some people considered the lack of templates/generics in Java version 1.4 and earlier to be a benefit, stating that the C++ implementation is too difficult to master or could lead to sloppy object designs. Although it is true that a well-designed object library or framework can mostly eliminate the need for templates/generics, as is often the case in programming, if the feature exists and it is helpful, you should use it.

Generics are considered one of the major changes to the 1.5 version of the Java language.

C#

C# (pronounced C-sharp) was created by Microsoft as a new programming language in late 1999/early 2000 to be released with their .NET endeavor. Its programming paradigm is very similar to Java, and much like Java, C# implements a sort of tokenized bytecode system with the promise of multiplatform compatibility. In 2000, the EMCA approved C# as a standard language at the request of Microsoft. This was an unusual thing for Microsoft, because it meant that the C# language was now a defined standard that Microsoft would not be able to alter as they might see fit. Many in the industry saw this as a benefit of C#, giving it an identity independent from Microsoft.

The theory behind C# is similar to that of Java, in that it is a language that can be used to create multiplatform programs. In practice, however, Microsoft views C# as a means of creating programs on Windows platforms. Although open-source projects exist to port the system to other platforms, their success is not guaranteed , and Microsoft has made no announcements of support for non-Windows platforms.

In Windows, a typical program is composed of a binary executable file. That is to say, the source code for the program is compiled into a machine-executable format. .NET still provides this means of software development, although it s specifically referred to as unmanaged code.

A new model under .NET mimics the Java platform: An application called the Common Language Runtime (CLR) is used to interpret and execute bytecodes of instructions, tokenized from various source modules. This is identical in concept to how the Java Virtual Machine (JVM) works. The .NET CLR has two distinct differences from the JVM:

  • The primary language for the CLR, C#, is now a standardized language. Whereas Sun maintains control of Java, C# is accepted by the European Computer Manufacturers Association as a standard language. This means that Microsoft cannot make changes to the language on a whim, and backward compatibility must be maintained .

    What s more, the CLR supports languages besides C#, such as C++, COBOL, and Java. Although these languages are not typically supported in their entirety, they are implemented in majority.

  • The CLR supports compilation to native machine code for improved performance.

Also, like the JVM, the CLR maintains control over executing code, and has the ability to stop malicious programs. Like in Java, CLR programs tend to be slower than their machine-executable counterparts. Programs written for the CLR are referred to as managed code.

The CLR also provides other features, such as the implementation of a standard class set and a Common Type System for implementing data types in the same manner (a time object is the same in C#, Managed C++, and Managed COBOL).

Reusable Code

C# contains a vast amount of reusable code, just like Java does. Using the .NET Framework classes, support for database connections, images, Internet connections, and so on are provided.

Performance: Interpreted and Compiled Code?

Like Java programs, C# programs are compiled into a tokenized bytecode that a separate program can understand and execute. The Common Language Runtime (CLR) is the program that works with the tokenized bytecode of C#.

The CLR actually goes beyond a virtual machine approach, however, and actually compiles applications on the fly to native code, thus improving their performance. In the case of web server applications called often, such as ASP.NET, the results of the compilation are cached and reused, again for better performance.

So C# goes through distinct steps: Programs written in it are first compiled into a CLR-compatible format (similar to what Java does), and then compiled into native machine code by the CLR the first time it is run.

C# provides native support for COM components and the Windows API, as well as restricted use of native pointers. This means, though the language may not be as fast as a direct executable application (because of CLR interpretation), performance will typically not be as poor as that experienced by Java applications.

Security

C# implements security control like Java in a sandbox format, but the basic control is set up by an assembly, which helps define the operations a particular program or class is allowed to perform.

Unlike Java however, C# does support pointers to directly operate on memory, although their usage can be controlled by security settings.

Portability

C# is theoretically portable, but no non-Microsoft operating systems currently can use it. This is not to say it is completely nonportable, however. Microsoft also has the handheld operating system currently called Windows Mobile (formerly Windows CE, Windows PocketPC, PocketPC 2000, and so on), which is distributed with the .NET Framework and the CLR needed to execute C# compiled programs.

As with Java, C# can be used on web servers, desktops, and handheld devices, as long as they are running the appropriate version of Windows.

Garbage Collection

Like Java, C# implements automatic garbage collection. Although destructors can be declared in C#, it is important to note that they are called when the garbage collector determines an object is no longer reachable by code and when memory space is needed.

Destructors have the same format in C# as in C++:

 ~ClassName(); 

The garbage collector will invoke this method automatically, when it sees fit.

In the event you are handling nonmanaged (non-CLR) resources, you may want to force the garbage collection for the object. In order to do this, you must declare your class to implement the Idisposable interface, and you must also provide a Dispose method. A typical example follows:

 using System; 
class Testing : Idisposable
{
bool is_disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!is_disposed) // only dispose once!
{
if (disposing)
{
// Not in destructor, OK to reference other objects
}
// perform cleanup for this object
}
this.is_disposed = true;
}
public void Dispose ()
{
Dispose(true);
// tell the GC not to finalize
GC.SuppressFinalize(this);
}
~Testing()
{
Dispose(false);
}
}

User Interface

C# does provide rich user interface support, but C# is limited to the Windows operating. C# also provides the ability to use existing ActiveX controls in a simple manner.

Like Java and its JavaBeans, the C# language was designed to facilitate easily creating components or add-ons that can be tied to the IDE used to develop programs. This means that you can easily create new controls of your own design and quickly and easily use them as a native part of the IDE s component gallery.

Multiple Inheritance

C# does not support multiple inheritance as C++ does. It does support interfaces, however, in a fashion similar to Java. As with Java, interfaces define code that must be written, not code that is reused.

Generics/Templates

C# does not provide support for templates or generics as C++ and Java 1.5 do.

Assemblies

An assembly implements the set of information for one or more of the code files shown in Table 12-2.

Table 12-2: Assembly Implementations

Assembly

Description

Versioning

Groups modules that should have the same version information.

Deployment

Groups code modules and resources that support your model of deployment.

Reuse

Groups modules if they can be logically used together for some purpose. For example, an assembly consisting of types and classes used infrequently for program maintenance can be put in the same assembly. In addition, types that you intend to share with multiple applications should be grouped into an assembly, and the assembly should be signed with a strong name.

Security

Groups modules containing types that require the same security permissions.

Scoping

Groups modules containing types whose visibility should be restricted to the same assembly.

Assemblies are nothing more than text files similar to source code. They can be embedded within a CLR-executable program or defined outside the CLR for multiple files. Many programs can include an assembly in the single executable files. The following is a brief example of an assembly for a C# project:

 using System.Reflection; 
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]



OOP Demystified
OOP Demystified
ISBN: 0072253630
EAN: 2147483647
Year: 2006
Pages: 130

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net