Dapat satu lagi artikel jadul, yang masih relevan untuk disimak.
Visual C++ Versus Delphi
Last Revised: 3/12/00. If you have any comments or suggestions or corrections please send them to me at firstname.lastname@example.org . Thank you.
The purpose of this paper is to provide Visual C++ programmers with a comparison between Delphi 5 Professional Version with Visual C++ 6 Professional Version, with an eye toward what a programmer needs and wants from his or her development tools in actual use.
When one looks back at the history of Visual C++ and Delphi, one can get an appreciation for the radical differences between them. Visual C++ traces its roots back into Microsoft C/C++, back when MS was the underdog in the C++ market. Delphi traces its roots back into Borland’s Turbo Pascal, which is itself derived from the first Borland product ever, a Pascal compiler that revolutionized the world by virtue of it’s amazingly low price, and made the name Philippe Kahn a household name among programmers. Like Borland, MS originated out of a language product, Basic, but then, unlike Borland, soon moved on to operating systems, office productivity suites, and a host of other products. Borland achieved a good deal of market share in the desktop database market, and even owned the spreadsheet Quattro Pro back then, but a costly legal battle with Lotus over the spreadsheet interface, and the movement of client server databases into the desktop realm eventually eclipsed both of those mark ets. Both company’s development tools reflect these different histories. In each case, their top-selling language product is based on their very first language product. Visual Basic is descended from Basic, and Delphi is descended from Borland Pascal. Visual C++ competes against Visual Basic for attention and resources within Microsoft, and so has never been made RAD the way Visual Basic and Delphi are RAD. This is probably the biggest difference between Visual C++ and Delphi.
One interesting difference between Visual C++ and Delphi is that since Borland at one time practically owned the desktop database market, Delphi comes completely equipped to create and handle Paradox and DBase tables through direct drivers in the included Borland Database Engine, rather than relying on ODBC. Everything you need to create desktop database applications is included in Delphi 5 Pro. Microsoft owns two desktop databases too, FoxPro and Access. As far as I can tell, Visual C++ 6 Pro has no included support for FoxPro and Access beyond the classes for dealing with DAO and ADO, both of which are the next evolutionary steps after ODBC. DAO used to be the way to get to Access in Microsoft development tools, but it wouldn’t multithread. Now the preferred data model is ADO which can be threaded and has a number of enhancements that make it far better for database access than DAO. Code to use ADO looks pretty much the same in Visual C++ as it does in Visual Basic, as ADO is a h igh-level layer of abstraction that encapsulates the details of connecting, maintaining and caching connections and refreshing data. The biggest drawback of using Visual C++ with ADO, versus Delphi with the BDE, is that Visual C++ requires you to write all the interfacing code yourself, and offers you little in the way of database components that will alleviate the need to do this. Visual C++ allows you to use ActiveX controls that provide some of this functionality, but ActiveX has a number of problems of its own, such as the fact that they are binary controls and so must be included in their entirety even if only a small part of their functionality is required.
Delphi comes with a number of database components that integrate so well with the Borland Database Engine that it is possible in Delphi for even a mere beginner to create a fully functioning database application in minutes, without writing any code. Even a seasoned Visual C++ programmer will be hard-pressed to be able to achieve this kind of speed, as Visual C++ has nothing at all that is comparable to the database components in Delphi.
Compiler and Linker
It should be pointed out that Delphi produces highly optimized machine code that is usually not noticeably different from VC++ code in speed of execution. What is amazing is that Delphi creates this code in a fraction of the time it takes VC++ to produce the equivalent. While the average compile in VC++ is several minutes, the average compile in Delphi is just a few seconds. I have never come across what I consider a totally adequate explanation for this difference in terms of the usefulness of the language features that cause this difference, but the common explanation is that C++ is much more difficult to parse, and can contain very deeply nested include files. Pascal is as fully Object Oriented as C++, in fact more so in my opinion, and offers roughly the same power as C++, and probably greater robustness. On the other hand, Delphi lacks a macro preprocessor, operator overloading and templates, which collectively are traditionally blamed for the slower C++ compiles. In Delphi the lack of templates i s dealt with by enforcing a single-inheritance object hierarchy. All objects in Delphi descend from Tobject, which gives them plenty of interesting properties and run-time information, and this means that you can use RTTI and polymorphism to accomplish in Delphi what templates accomplish in C++. Some people find the C++ template approach is cleaner, more concise, and easier to use, other find the single-inheritance approach better. Of all the features of C++ the macro preprocessor is the most abused, particularly by Microsoft. It is debateable whether all these features are worth the much greater extra compile time in C++.
In response to these slow compile times, C++ vendors have introduced incremental linkers and precompiled headers, which do speed up the average VC++ compile. Even with these enhancements turned on, a second (partial) compile is still longer than the equivalent Delphi full compile. Furthermore, these enhancements are not flawless or in my opinion even trustworthy. It is a common occurance in VC++ for the use of precompiled headers and/or incremental linking to result in several problems 1) a mismatch between breakpoints and their code, 2) incomplete integration of new code 3) incorrect logic and 4) inability to compile because it is misreading the header files (usually this manifests itself as an “Unexpected End of File”).
One feature of both compilers and linkers is the ability to recognize and remove dead code. Like VC++, Delphi will only compile/link in the code that is actually called, but unlike VC++, it will give you a visual cue in the editor to show you what lines of code were live and which lines were dead, so that you could tidy up your code by taking out dead functions, etc. Delphi does this by displaying a small blue dot in the left hand gutter for lines that are live, after a compile and link. There is no way to know which code is live or dead in VC++. Having said this, it is important to realize that Delphi has a minimum code size of around 200K, because certain sections of the Visual Component and Run Time Libraries are called in every single application. (You can get smaller apps by not using the VCL and relying on Win API calls instead.) In VC++ these types of libraries are DLL’s that are assumed to be on the client’s machine, so a Visual C++ app using MFC can be incredibly small. (This is one of the advantages of being the one that supplies the operating system. You can “cheat” by making something needed by your development tools be a part of the operating system. When Borland asked MS if MS could include, along with the operating system, DLL’s for use by apps made by the Borland tools, MS said no. Only MS tools get this advantage.) This introduces the very slight risk that a future installation over those DLL’s would render the VC++ app suddenly unuseable. This occurred with the
second service patch for Visual Studio 6, for example (it was fixed with the next patch). On the other hand, the default database model in Delphi relies on DLL’s that make up the “Borland Database Engine”, which together will add 3-5MB to a project. In addition, this also exposes the program to the possibility of the DLL’s being overwritten at some point with something incompatible, though Borland tries to prevent this by requiring only approved installers be used for the BDE.
Delphi does offer the ability to create and use DLL’s, both as the generic Windows variety of DLL callable from any language and as an easier to use Delphi-specific version Borland calls “packages”.
An interesting aside is that Delphi can create and use C++ OBJ files created and used by C++ Builder. Though it is a difficult and time-consuming to use classes across that boundary, procedural code is not difficult to use that way. The only real difficulties arise in dealing with references to the C++ run-time libraries, which of course do not exist in Delphi and so would result in unresolved external references.
Finally, the error messages reported by VC++ are much more cryptic than the straight forward messages reported by Delphi’s compiler and linker. This is especially true if one is doing template work, such as using the Standard Template Library. Such error messages can literally wrap across the full screen width three or four times.
Components are a very big difference between Visual C++ and Delphi. Visual C++ components are ActiveX controls. Delphi can use ActiveX controls, even more easily than Visual Basic, but can also use VCL components which link directly into the executable as mentioned above. Most Delphi users will only use an ActiveX control if there is no Delphi control that will do the job (which is unlikely). Delphi can be used to create Delphi components (which will also work in C++ Builder) as well as ActiveX controls that will work in just about any development environment. Delphi has a special wizard for creating ActiveX control out of VCL components, for code reuse. Visual C++ has libraries and wizards for creating ActiveX controls but it comes with no preinstalled controls.
It is in the area of components that Delphi really is leagues ahead of Visual C++. This superiority is twofold.
1. Third-party Delphi components are plentiful and most are free. There are plenty of high-quality components for all but the most esoteric of needed functionality. They are relatively easy to install.
2. Delphi comes with a large set of pre-installed components that greatly speed up development. There are several categories of these components:
* Win API: windows GUI components like the Listview, etc as well as standard windows dialogs. There are many more components than there are generic Windows controls.
* Data Access: Data retrieval components for handling SQL, database management and batch transfers, as well as components that form the base for the Data Controls. Also includes data modules, which have a design screen allowing diagramming of data relationships.
* Data Display: components for displaying database data, such as images, text, etc. in a variety of different appearances such as grids, individual windows components, etc.
* Interbase Express: statically linked components for access to the Interbase relational database, without the need for any DLL’s.
* Internet Socket and HTTP Controls: controls for socket communications and dynamically created HTML.
* Fastnet: Internet controls from Netmasters, that provide out-of-the-box functionality for HTTP, NNTP, SMTP, POP3, etc. In prior versions these had been ActiveX controls but are now statically-linkable VCL components, and are threadable, by the way.
* QuickReport: controls for creating printed and previewable reports.
* COM Servers: controls for controlling Office applications Word, Excel, Project and Outlook as well as Internet Explorer via COM.
* TeeChart Charting and Graphing: controls for various charts and graphs.
These components are for the most part stable and tested, as well as being fully documented in the help system, except for the COM Servers (as of this writing). In addition to individual components, Delphi also allows the creation and use of component templates which are a bunch of components together with the code that ties them together. You can select several components and turn them into a single component that you can drop on any form like a regular component, with all the intraconnecting code intact. In addition, one can use Frames to create the equivalent of a container form that can be contained in other components and screens. Components can be easily tied to actions by using ActionLists, which handle the enabling and disabling of connected components and their captions and associated event handlers. This greatly speeds up the creation and control of complicated GUIs and enables a level of abstraction between GUI and code. In addition, Delphi comes with a convenient type library editor for the type libraries associated with COM objects.
The Visual Component Library in Delphi 5 is a more recent creation than MFC, and reflects a philosophy that prefers greater abstraction away from the Win32 API. While MFC lacks support for such basic things as setting component colors, these and a myriad of other properties can be set in VCL components with a single assignment. Programmers don’t have to look up the appropriate Win32 API calls needed to set a Window component’s specific property like color, they simply write one little assignment or set one single property in the Delphi property editor. This results in code that is much more readable and maintainable, as well as being easier to write rapidly.
The VCL also contains classes for non-visual aspects of Windows programming, such as wrapper classes for handling INI files, the registry, the clipboard, media files, timers, hotkeys, file wildcard masks, AVI files, services, streaming, drag and drop, COM, printing, string lists, etc. Many of these classes have no counterpart in MFC, or are much more lightly embodied than in Delphi. Take the TstringList class in Delphi as an example. In VC++ one would use a CStringList. CstringList has members for adding, retreiving and removing members of a list, but Delphi’s TstringList adds sorting, easy import and export of comma-delimited data, name=value parsing, and file and stream input and output. The Tclipboard, Tregistry, TregIniFile and Tinifile classes are other examples of VCL classes that have no equivalent in MFC, but encapsulate a lot of grunt-work Win32 API coding that otherwise has to be done by hand. Overall, the general rule seems to be that over 95% of the Win32 API programmin g a person needs to do is done for you in a VCL control or class.
One field in which the VCL is inferior is in its support and accommodation of multithreading. While the VCL supplies a class that is trivially easy to use for running worker threads, things get complicated when threads have to interact with the VCL, because most of the VCL is not thread safe. You have to resort to using a supplied Tthread class method called “Synchronize” that waits for the main thread to enter its message processing loop, and then updates the VCL within the main thread, or you have to use an alternate method like messages to communicate between the non-primary threads and the main thread. You can’t directly access any of the visual VCL components from within any thread other than the application’s main thread. MFC can handle multithreading without such ruses, in fact you can place message pumps within a thread and have a window act like its own thread.
The component model in Delphi has a very nice advantage over the Microsoft component model, ActiveX, and that is that Delphi components can be statically linked right into the executable. Not only is this more convenient (and much smaller) when it comes to deployment, but it also mean
s the components share in the benefits of modern linker intelligence. If you use one small bit of functionality from a large extensive component, you only suffer the overhead of the functionality you actually use, and that functionality will be optimized with your code to boot. To get the equivalent in Visual C++ you would need to have the code itself, which works OK for freely distributed code but not so well for commercial components. In addition, these kind of C++ code libraries tend to be much more expensive than their equivalent Delphi components.
There are plenty of third-party Delphi components that don’t come with Delphi, and most of these come with source code. While the components that come with Delphi or the supplementary CDROM are stable and tested, components from other sources may vary quite a bit in quality. Creating advanced Delphi components is an interesting intellectual challenge that may exceed the attention spans for some individuals, resulting in components that make Delphi unstable. Delphi doesn’t run component code inside any kind of protective sandbox, and so an errant component can pull Delphi down into oblivion. The most common manifestation of an errant component is that Delphi will not close down gracefully, maybe even giving an endless cycle of access violations that requires a hard reboot. While this is rare, if you try enough components from unknown sources and/or beginners, you will eventually run into the problem. This will be the first question an experienced Delphi programmer asks you if you complain that Delphi is unstable: “What components did you install?” Ninety nine times out of a hundred it is some component that is causing the problem. All such third-party components should be thoroughly checked out before being used in any important Delphi program.
It is in the IDE that VC++ 6 comes the closest to Delphi 5, as both have heavily incorporated the intellisense features that first debutted in Visual Basic. In Visual C++, the method completion feature that pops up when you type a class or object name and “.”, “::” or “->” has a nice additional feature that shows fly-by hints for each item in the drop down listbox, a feature Delphi lacks in its version of intellisense. Visual C++ also has automatic formatting as you type (though it is not as advanced as Visual Basic’s, which will even correct or respond to your changes in the capitalization of variable names). Delphi has no automatic formatting. In addition, Visual C++ seems less memory and resource intensive than Delphi 5, which has sometimes frozen on my Windows 98 machine with 96MB of RAM when I forgot to close other programs that were running at the time. Delphi 5 takes a seeming infinity to start, and after a few debugging sessions seems to take an infinity to close, often j ust dying out with access violations or illegal operations if Program Reset was used to cancel out of one or more debugging sessions. Delphi doesn’t use a virtual machine to isolate debugging sessions nor component code and the result in Windows 98 seems a tad precarious, at least on my machine. It seems that the two competing forces of high resource use and components and debugging sessions not being isolated are a bit too much for the jerry-rigged operating system known as Windows 98. Often an errant Delphi program can pull Delphi down with it. I have sometimes seen this happen with Visual C++.
Those caveats aside, The Delphi 5 IDE offers several very nice advantages over the Visual C++ 6 IDE. Once you step away from the VB-inspired intellisense, the Visual C++ IDE starts to show its age.
In contrast to Borland’s clean “two-way tools” the proprietary comments cluttering VC++ code from the Wizards are kludgy and incredibly dated. Where Delphi offers a nice clean Object Inspector to set properties and event handlers, Visual C++ is still relying on the ClassWizard from five years ago, back when cluttering code with all sorts of “do not touch!” markers was how all code generators worked. While it may have been innovative back then, it is just an anachronistic annoyance now. Code generated by Delphi has no markers at all and there is never a disconnect between the tools and the code no matter how many changes one makes. The mechanism in Delphi simply works better and the code is much, much cleaner and therefore easier to change and maintain. In Visual C++ the ClassWizard can become downright useless if any of the proprietary code markers are accidentally altered.
As an editor, the Delphi IDE offers several advantages as well. Unlike the class view in Visual C++, the code explorer in Delphi operates on any file that is opened, whether or not it has been added to the project yet. In addition, the code explorer is accessible during debugging. That is very convenient when porting code between projects or looking at code samples. In addition, it shows other useful information, such as what other files the current file is directly dependent on. A separate browser is also available in the IDE, which does require a compile, and is not visible during debugging, if one wishes to see references and the inheritance tree. This browser is different from the browser in Visual C++, with the predominant difference being that it doesn’t disappear once you click on a reference. Another very useful Delphi IDE feature is class completion, which creates declarations for undeclared definitions, creates skeleton definitions for undefined declarations and automatically creates the access methods and procedures for properties. This is really convenient when writing a bunch of method definitions. A single key-stroke will toggle between a declaration and definition. Delphi also offers code browsing, where you can go forward and backward through code references the same way you can go back and forth through hyperlinks on the internet. You pause the mouse over an identifier and press the Ctrl key. The identifier becomes a hyperlink to its definition. Click and you are there, with a highlighted “back” button in the IDE to take you back. This is like a multilevel reference browser, where you can trace deep into a tree of function calls and then quickly retrace your steps back home. Visual C++ has nothing like this, though you could achieve the same result in Visual C++, with much more work, by using bookmarks and the browser (which you could also use in Delphi).
Together the collective result of all these Delphi IDE innovations is a much greater, much more holistic understanding of the code than you will usually get using the Visual C++ IDE.
In addition though there are other nice touches to the Delphi 5 IDE that go beyond this. A code template feature lets you store code snippets that can be retrieved with simple keystrokes, like live macros that expand during editing. A ToDo list is integrated into the IDE which makes it very convenient to use, offering a type of project/feature tracking. You can associate to-do items with specific locations in the code or with the project in general. Getting help on any specific item in code is also more convenient, as it takes only a right click and menu selection. If you select an identifier this help look-up is intelligent enough to know to look up the identifier’s type rather than engage in a futile search for the identifier string itself. In Visual C++ you have to copy the name of the item you want to look up, and then open up the help system (a localized MSDN) and paste the string into a combo-box. Unfortunately, you often must engage in an additional step of limiting the scope of the search or you will end up with all sorts of J++ and VB references that do no good. This marks the major difference between Borland and Microsoft help systems. The Microsoft help resources are massive, but this makes finding relevant information unduly time-consuming, whereas the Borland help systems are more specialized to the task on hand. I prefer the Borland approach, as I can always use a mere two mouse clicks (or one keystroke) to get the information I need. It is my personal opinion that MS s
hould include more code samples for specific help items, instead of large project-scale samples. Another nice feature of the Delphi IDE is that the project manager shows the full absolute path for project files. In Visual C++ if you need to move files around you may be dismayed to discover that while the project files and IDE are using absolute path names, they do not update these nor show you these absolute path names. As a result, forking code can be catastrophic, as the new proje ct still relies on the files from the old project location even though the IDE gives no visual clue to this fact. In Delphi, you can freely move projects around without having to do any manual fix-ups, as Delphi uses relative paths and in any event shows you the full absolute paths it is using. In Visual C++ it usually just isn’t so easy. (BTW, the project window in Delphi now supports multiple executables and projects, even allowing convenient drag and drop of files) The Delphi IDE has another unique feature in that you can save multiple “desktops”, which are layouts, whereas VC++ 6 will only save and use one layout. You can switch between desktops simply by selecting the new desktop in a combo-box on the Delphi toolbar. The IDE will automatically switch to the user-configured “Debug” desktop when debugging. The Delphi IDE also makes it much easier to work with the Version Information resource for your executable, as well as optionally auto-incrementing the build number in the version number. Boo kmarks in Delphi are a little easier to use than in Visual C++, as they are available in the right-click menu, and they do not accumulate across different sessions. In Visual C++ my bookmarks list is now several dozen long and deleting them only temporarily deletes them, with the deleted bookmarks returning in the next session.
When it comes to debugging there are some major differences between Delphi 5 and Visual C++ 6, which can be summed up by saying that Delphi’s hunger for windows resources can cause problems for debugging applications under Windows 95/98. Some of the additional features in Delphi 5 make certain debugging tasks much, much easier. Delphi has all the debugging features found in Visual C++, but adds several more. In Delphi, you can set breakpoints on the loading of DLL’s so that you can see the exact line of code that precipitates the module load. Then you can look through the module view and see what the DLL exports, the absolute path of the DLL’s that are being used, as well as the code files used by your executable. You could even go right to the assembly language in the DLL’s that corresponds to the exported symbol of your choice. In addition to the “Module Load” breakpoints, there are also “Data Breakpoints” that will trigger when a specific variable is changed. All breakpoints in Delphi can be conditional, can execute an expression, log to the event log, and can enable/disable groups of breakpoints or all remaining breakpoints. In fact you could choose to have a breakpoint simply log an expression and go on without stopping. Delphi 5 has full provisions for remote and DLL debugging. In addition, the inspectors in Delphi allow you to change any variables you wish at any point, something that Visual C++ very frequently balks at, without explanation.
As mentioned previously, in Delphi you can access the code explorer that gives you a convenient point and click tree structure for browsing code, which is equivalent to the Class Explorer pane in Visual C++ 6.0, whether you are editing or debugging. In Visual C++ the equivalent pane is not visible during debugging sessions, though you will often have the greatest need for it at that point.
Delphi also has data breakpoints and a very useful thread view, both of which can be very handy when trying to track down elusive bugs. (The Visual C++ thread view is a modal dialog box, which reduces its usefulness) I have found the integrated to-do list functionality very useful during debugging, as I can make a note of the bug right then and there in the IDE.
No discussion of debugging would be complete without at least a passing comment on Visual C++’s “Edit and Continue” feature, which originally debutted in Visual Basic. The idea looks good on paper and in marketing blurbs, but in practice the feature is often virtually useless. The main problem with this feature is that it requires the incremental linker, which itself is not reliable in my humble opinion. I have seen too many instances of the incremental linker (and partial builds as well) simply not fully incorporating all changes to code. Even if you get past the need for accurate compiles you are faced with the fact that there is no way the compiler could recreate the full results of any code change if it occurs in the middle of a multiply iterated loop, for example. The third problem I had with the “Edit and Continue” feature was that I could never get it to work very well. The most common problem would be an inability to invoke the incremental linker for some reason. This marke ting feature is simply not good enough nor robust enough to offset the loss from the frequent inability to alter variables during debugging.
Another advantage of Delphi over Visual C++ is the difference in compile and link speeds, with Delphi’s blinding speed being a huge advantage here. The fastest C++ partial compiles/links are still several times longer than Delphi full compiles/links and this can make a big difference when one is trying to fix and test bugs quickly. The rapid turnaround possible with Delphi makes rapid-fire debugging possible, while debugging with Visual C++ is hobbled by the slow C++ compiles.
Both Object Pascal and C++ are full Object Oriented languages. Both languages also have roots in procedural languages that preceded the integration of Object Oriented principles. This means that both languages allow you to incorporate Object Oriented principles as little or as much as you want, subject to certain caveats I will cover very shortly. There is a common misperception among many Visual C++ programmers that Delphi is just like Visual Basic, but using Pascal instead of Basic. This is not true. In the spectrum of languages, in many ways Object Pascal is to Pascal what C++ is to C. Both languages are rich enough that an intelligent, inquisitive person will find themselves unlimited by the language. Object Pascal is in the same league as Java and C++. Some say it is easier to learn than C++, but this is hard for me to gauge, as I already had several years of experience in C++ by the time Delphi came out. One thing that can be gleaned from the newsgroups and discussions with novices is that it is indeed harder to learn Object Pascal than Visual Basic.
A person who knows only one of these two languages does themselves a great disservice by not learning the other. Object Pascal and C++ are full and rich languages that each have their interesting and challenging idiosyncrasies and hidden treasures. Each language has a different feature set, and so require of the good programmer that he approach the same problems in different ways. I personally find that knowing and using both is a good way to keep oneself interested and open in programming as an intellectual exercise, an art, and a career. If, like myself, you feel that “real programmers” know at least two different computer languages well, these two are extremely good candidates for one’s toolbox. There is no significant loss in power in going from either one to the other.
Avoid falling for the common misperceptions that Object Pascal is a “toy language like VB” or that C++ will be forever luring the gullible programmer into wild pointers and memory allocation errors that are impossible to fix. I have been using Object Pascal since Delphi came out and I have not run into any of the types of limits that programmers associate with beginner’s tools like VB. Likewise I have been using VC++ in my career and I haven’t had a single wild pointer or memory allocation error in th
e past year or so, despite coding a complete commercial app and it’s update in that time. Both languages are more than adequate for most of the programs you will need to write, and are complex enough to keep inquisitive minds entertained.
There are some important caveats to keep in mind however. Both Delphi and Visual C++ come with frameworks for creating Windows programs and both tools bend their language around their framework or for its benefit. Visual C++ does not by default create nor read ANSI C++ if you use MFC. You can tweak ANSI C++ code to get it to compile in both ANSI C++ compilers and the Visual C++ compiler with MFC, but you can not take MFC-based Visual C++ code and use it in anything other than the Visual C++ compiler. (There is one exception to this, in that C++ Builder 5 has an MFC compatibility switch for it’s compiler.) Likewise, Object Pascal created in Delphi is also mostly proprietary. It can compile most generic and old Pascal code, but it doesn’t create it. In both cases, there are third party paraphenalia that attempt to remedy these limitations, but there is inevitably the additional possibility for mischief in these schemes.
Object Pascal is a language created and used exclusively by and for Delphi and as such it represents are far greater departure from traditional Pascal than is represented by the departure of Visual C++ from ANSI C++. It also fits Delphi’s VCL far more tightly than the C++ created by Visual C++ fits MFC. Because there is not even any pretense to an ANSI-like standard for Object Pascal, Borland has freely adapted Object Pascal to Delphi’s needs. The result is a language that gracefully accomodates the Property Method Event model embodied in Delphi and makes it easy to create robust readable Windows code. On the other hand, Microsoft had to minimize the changes to C++ that would accomodate MFC, and the result of this, and the fact that MFC is much older than the VCL, is that a more procedural methodology results in the use of macroes, untouchable cryptic markers in comments and things like the “message map”, which is an untouchable but visible block of code that matches messages with functions. The Delphi approach is far cleaner and easier to create, read and maintain, without sacrificing the power to get under the hood and fiddle with the works if one so fancies. This reflects an apparent design philosophy in Delphi that the language should be subordinated to the goals of the tool, rather than the tool being subordinated to the goals of the language.
Aside from this, there are important differences that may be benefits or costs, depending on your point of view. Object Pascal does not offer multiple inheritance, templates, operator overloading, inline functions, preprocessor/macroes, globally accessible static class variables, nor nested classes. C++ does not offer virtual constructors, embedded procedures, sets, built-in messages, built-in interfaces, built-in OLE automation, built-in strings, or the “finally” construct. None of these are real impediments in either language, as alternatives exist for all of them that are more than adequate in creating the same results. For example, in Delphi the default calling convention is the register convention which passes parameters via registers instead of the stack (thus usually avoiding the creation of a stack frame altogether), avoiding the same overhead avoided by inline functions.
C++ and Object Pascal have a great number of syntactical differences, but this should not lead one to conclude that they have great number of conceptual differences. A C++ programmer who already knows Object Oriented principles will find it rather easy to learn Object Pascal. Because the same concepts apply to both languages, the only real differences are in the implementation of those concepts. This conceptual overlap not only provides the seasoned C++ programmer an easy entrance into Delphi programming, but it also means that knowing and mastering both languages will thereby strengthen his knowledge and appreciation of those underlying Object Oriented principles.
Delphi has a fair number of components, language features and classes for writing COM/OLE/ActiveX programs, components and servers, with particular strength on the creation and use of objects rather than servers, though Delphi 5 did add some significant improvements to the support for COM servers. Using COM objects in Delphi can be as trivially simple as in Visual Basic, or as complicated as you want. The Delphi approach to COM is thoroughly component oriented, in fact a Delphi wizard will create components on the pallette that wrap around ActiveX controls and servers, so that you can drop them on your forms and set their properties in the design-time Object Inspector as if they were normal Delphi components. This is an entirely different approach than one finds in Visual C++’s ATL, because it is much more RAD in it’s philosophy than the ATL, and seasoned ATL users may unfortunately see the Delphi approach as indistinguishable from the Visual Basic approach. It would be incorrect though to assume that the Delphi system is a limited black box like the Visual Basic system. All the code for the COM related classes is available, one can use inheritance to alter most of the behaviour, and the Delphi COM classes are layered enough that you can choose from several different levels of abstraction. The highest levels of abstraction so encapsulate the details of COM that you can use COM without knowing COM, for better or for worse. Before the ATL, COM in Visual C++ was a lot of work that often were exercises in frustration for many, particularly those who don’t have much knowledge and/or experience in COM. The ATL has greatly simplified and lessened this burden, and in the hands of experienced programmers seems to be more than up to the task of creating COM servers and controllers, but it is impossible to improve upon the simplicity offered by Delphi, which sacrifices nothing in therms of power.
It is important to realize that these two different libraries, the VCL and the ATL, seem to have been designed with different goals and philosophies in mind. The VCL seems to have been designed with more emphasis placed on the creation and use of COM/ActiveX controls and controllers, with the ATL being better suited to the creation of COM servers. However, in this most recent edition of Delphi, Borland has tried to position Delphi as a better creator of COM servers than before. Differences in architecture and philosophy remain though, and are significant enough to present a delightfully challenging intellectual exercise for anyone crossing over from one to the other. Interfaces are a built-in feature of Object Pascal, which leads to some very clean, easy to read code. COM Interfaces in Visual C++ are not built-in to the language but so are not as cleanly coded, but work quite well too. However, Delphi interfaces support multiple inheritance, though Delphi classes do not. This disparity means that one can not descend from both a class that provides the implementation for the interface methods and another class that provides the implementation for the methods in one of the other interfaces from which it inherits. Instead, one must use a VB-like approach where one integrates an object of this other class into the target class as a private member and assign it as the implementation for the interface using an “implements” keyword. This may put off seasoned C++ programmers who will be scratching their heads wondering why true multiple inheritance couldn’t just be added to Object Pascal to more easily accomodate interface implementations instead of requiring them to use this VB-style workaround. More than anything else, this curious situation seems to be a good reason why, though Delphi is better for COM controllers, Visual C++ seems better for COM servers, at least those inheriting a significant amount of their functionality from a pre-existing code base.
COM code in Object Pascal is far cleaner and easier to read than in C++, due to the way interfaces are built right into the
Object Pascal language.
If you are like most Visual C++ programmers you would say that code created with Visual C++ will most likely be faster because “everyone knows that Visual C++ is what you use when you need speed”. You would be wrong to make such a blanket assumption about Delphi’s ability to produce optimal code. See Jake’s Code Efficiency Challenge for real world examples of attempts at optimal code in both languages. The compiler and linker in Delphi is on the same par as the Visual C++ compiler and linker, producing fast, rather well-optimized code. Granted, one has more optimization choices in Visual C++, being able to optimize for size or speed, or to toggle individual optimizations, and there are certain speed oriented C++ code features like inline functions that have no equivalent in Delphi. However, given all this it remains the fact that there is no significant difference in the speed of the code created in Delphi versus Visual C++.
What is a fact is that there are idiosyncratic weaknesses in the code libraries that can impede code efficiency. In the case of Delphi, the compiler and linker are top notch, but some parts of the VCL are not so strong when it comes to manipulating strings, such as the StringReplace function which results in timings that are two to three times their Visual C++ CString::Replace equivalent. Likewise a stray container choice can decelerate string manipulations in Visual C++, such as using CStringArray instead of CStringList. Careful coding (and time-testing the code) can have a big payoff in both tools.
It is in database support that Visual C++ really lags behind. While Delphi offers drag and drop simplicity in creating database-centric user interfaces, Visual C++ requires the user to code almost everything themselves, from beginning to end. The difference here results in several orders of magnitude of difference in productivity. While Visual C++ programmers are busy looking up how to create a connection string for ADO, Delphi programmers will already be unit testing their creations.
Database support in Delphi 5 Pro, comes in several categories, the Borland Database Engine, the ADOExpress Components for native ADO, and the InterbaseExpress components for Borland’s (soon to be Open Source) Interbase. All three support the drag and drop database-aware controls that come on the Delphi pallette, which include edit boxes, lookup comboboxes, grids and so on. The Borland Database Engine supports SQL for local desktop databases like Paradox, DBase, Access, ASCII, Local Interbase and anything for which you have an ODBC driver. The Borland Database Engine offers a plethora of features such as bidirectional scrolling, cached updates, connection control, transaction control, etc. even for databases that do not offer these features themselves. Support and unlimited licensing for Paradox and DBase tables is included straight out of the box for you to use in your applications without further fuss or expense. The only problems with the BDE is that it is based on a set of DLL’s thart you must distribute with your app and install using a tool like InstallShield, and this set of DLL’s has a rather large footprint of several megabytes. The new ADO components in Delphi are wrappers around ADO, which like ODBC, can access anything for which there is a driver. While ADO is a thinner database interface than the BDE, and not as easy to use, it offers the advantages of being familiar to Visual C++ programmers, coming with the new Microsoft operating systems, and offering a popular standard interface for Windows-based database access. In addition, the ADO components in Delphi are statically linked right into the executable, requiring no additional DLL’s to be distributed to PC’s that already have ADO. The ADO components in Delphi connect to the database aware controls on the Delphi component pallette just as easily as the BDE components, for drag and drop ease in setting up user interfaces for ADO-based applications. Finally, Delphi also comes with native Interbase controls for using Interbase as the SQL database to which database aware user interface controls can be connected. This has the potential upside that, because Borland has decided to make Interbase Open Source, these components will allow a programmer to quickly set up user interfaces for a free heavy-duty database they can freely distribute with their applications. Delphi 5 Pro also comes with Borland’s Web Broker technology which makes it easy to use these database access methods and components to create database-centric web pages and HTML interfaces for access from any browser.
Visual C++ comes with code libraries that ease, but don’t eliminate, the burden of writing your own ADO or OLE DB code. ActiveX controls that interface with these libraries allow programmers to put a visual user interface into their VC++ programs, but don’t offer anywhere near the speed and ease that Delphi offers for database programming, and must be distributed along wth the app. Visual C++ has nothing for Paradox or DBase tables, and no desktop databases come in the box for you to use and distribute if you buy Visual C++ pro by itself, though it will interface with Access if you obtain it separately. While Access is rather lightweight compared to Paradox tables, it might be adequate for many needs. Visual C++ will interface well with MS SQL Server and any database for which a C/C++ API exists, but still requires non-trivial coding to connect database data with the user interface. Microsoft has not announced any plans to make their SQL Server database open source.
Conclusions and Recommendations
The choice of a development tool is dependent on several different factors, some of which have nothing to do with the technical merits of the tool itself. No program is ever written in a vacuum, without being affected by outside forces. Programmers will often argue for their favorite tool as if it will be used in it’s ideal setting, with perfectly experienced developers wielding it with grace and might. The reality is often starkly different from the utopia taught by such zealots. The factors to be considered in choosing between Delphi 5 and Visual C++ 6 include:
* Existing Skill Set: The learning curves for both C++ and Object Pascal imply a start up cost that must be weighed in when programmers will be using them as novices. According to various sources there is as large as a 10 to 1 differential in the productivity of programmers. By the apparent consensus among many Delphi programmers, Delphi allows 2 to 4 times more productivity than Visual C++ overall, and the opinions among those Visual C++ programmers who have tried Delphi appear to be that the differential is much less than that. The moral of this story is that if you can find experienced, good programmers, you should probably consider using whatever tool for which they are experienced and skilled. Their skill differential may outweigh the tool differential, if they are highly skilled in one tool and know nothing about the other. Unfortunately, the reality of the marketplace is that the best programmers are very difficult to find and 9 chances out of 10 you’ll end up with a rookie who has lied on his resume. Since Delphi is gentler on novices than Visual C++, this would seem to be a point in Delphi’s favor, unless you already have a pool of experienced skillful programmers. Experienced, skilled Delphi programmers will demonstrate greater productivity than experienced, skilled C++ programmers, if you can find them.
* Existing Legacy Code: Rewriting code from scratch is an invariably expensive process, in the short run. Despite all the implied benefits of OOP for code reusability, reusing already existing code will cut more time off a project than anything else. Copy and paste in VC++, and drag and drop in Delphi, are still the predominant form of code reuse, and the more of a project’s code can consist of already-written code the more of a headstart you’ll hav
e. Programmers are notorious for wanting to rewrite everything in their own image and seem to almost universally suffer from the “not invented here” syndrome. This personality trait is very expensive. The person who walks into a place that has an already functional product that meets their needs and says he will completely rewrite the product from scratch using the latest OO techniques is a would-be charlatan who will drain your pocketbook in search of the Holy Grail of programming purity. This point also applies to those who refuse to use components and component based systems. Components represent the ultimate standardization of code reuse, and Delphi does components much more effectively than Visual C++.
* The Nature of the Required Programs: Both tools have their particular strengths when it comes to specific types of programs. Delphi is vastly superior to Visual C++ for the creation of database front-ends, as well as the rapid creation of rich, effective user interfaces. In addition, in those areas in which pre-existing Delphi components exist that are robust and powerful, Delphi again has the strong advantage. This includes internet and intranet applications, charting and reporting, database access, and advanced user interfaces. Where Visual C++ has an advantage is the creation of device drivers, COM servers, scientific, mathematical, and statistical applications, console mode programs, WinCE applications and small utilities. These disparate advantages fly in the face of the trend among development houses to standardize on a single development tool for all development. Rather, maximum productivity would be attained by mastering both tools and using whichever one was best suited for a given application. There is absolutely no reason why any good programmer could not be proficient and productive in both languages.
* Future Portability and Extensibility: As of this writing, the only area in which there is anything approaching certainty is that Borland has announced that their C++ Builder and Delphi products will come out in a Linux version in the middle of the year 2000 and that there will be an attempt to minimize the effort needed to port applications from the Windows versions of these products. While Borland has been uncharacteristically vocal about their plans for the future, Microsoft has been uncharacteristically mum about the future. What is known is that whereas it used to be possible to write Visual C++ apps for the Intel and Alpha chips, NT for the Alpha chips is now being dropped. This leaves WinCE for multiple platform Visual C++ programming. Unfortunately for this cause, WinCE is doing poorly in its market. In addition, it seems rather adventurous and probably unwarranted to believe that Microsoft will port Visual C++ to non-MS operating systems any time soon, given their track record so far, leaving adventurous cross-platformers to the ravages of third-party hacks and intermediaries. Borland, on the other hand, seems positively giddy over Linux lately. Also, on the database front, Borland has recently announced that their database, Interbase, will be open source (read “free”), and that they will be improving the integration of Interbase with Borland tools.
Copyright 2000 by John M. Jacobson. All Rights Reserved.