Professional C__ - Marc Gregoire [433]
Shifting Paradigms
One of the dangers of mixing C and C++ is that your program may start to lose its object-oriented properties. For example, if your object-oriented web browser is implemented with a procedural networking library, the program will be mixing these two paradigms. Given the importance and quantity of networking tasks in such an application, you might consider writing an object-oriented wrapper around the procedural library.
For example, imagine that you are writing a web browser in C++, but you are using a C networking library that contains the functions declared in the following code. Note that the HostRecord and Connection data structures have been omitted for brevity.
// netwrklib.h
#include "hostrecord.h"
#include "connection.h"
// Gets the host record for a particular Internet host given
// its hostname (i.e. www.host.com)
HostRecord* lookupHostByName(char* inHostName);
// Connects to the given host
Connection* connectToHost(HostRecord* inHost);
// Retrieves a web page from an already-opened connection
char* retrieveWebPage(Connection* inConnection, char* page);
The netwrklib.h interface is fairly simple and straightforward. However, it is not object-oriented, and a C++ programmer who uses such a library is bound to feel icky, to use a technical term. This library isn’t organized into a cohesive class and it isn’t even const-correct. Of course, a talented C programmer could have written a better interface, but as the user of a library, you have to accept what you are given. Writing a wrapper is your opportunity to customize the interface.
Before we build an object-oriented wrapper for this library, take a look at how it might be used as is to gain an understanding of actual usage. In the following program, the netwrklib library is used to retrieve the web page at www.wrox.com/index.html:
#include #include "netwrklib.h" using namespace std; int main() { HostRecord* myHostRecord = lookupHostByName("www.wrox.com"); Connection* myConnection = connectToHost(myHostRecord); char* result = retrieveWebPage(myConnection, "/index.html"); cout << "The result is " << result << endl; return 0; } A possible way to make the library more object-oriented is to provide a single abstraction that recognizes the links between looking up a host, connecting to the host, and retrieving a web page. A good object-oriented wrapper could hide the unnecessarily complexity of the HostRecord and Connection types. This example follows the design principles described in Chapters 3 and 4: The new class should capture the common use case for the library. The previous example shows the most frequently used pattern — first a host is looked up, then a connection is established, then a page is retrieved. It is also likely that subsequent pages will be retrieved from the same host so a good design will accommodate that mode of use as well. Following is the public portion of the definition for the WebHost class. This class makes the common case easy for the client programmer: // WebHost.h class WebHost { public: // Constructs a WebHost object for the given host WebHost(const string& inHost); // Obtains the given page from this host string getPage(const string& inPage); }; Consider the way a client programmer would use this class: #include #include "WebHost.h" int main() { WebHost myHost("www.wrox.com"); string result = myHost.getPage("/index.html"); cout << "The result is " << result << endl; return 0; } The WebHost class effectively encapsulates the behavior of a host and provides useful functionality without unnecessary calls and data structures. The class even provides a useful new piece of functionality — once a WebHost is created, it can be used to obtain multiple web pages, saving code and possibly making the program run faster. The implementation of the WebHost