Dr. Dobb's Journal January 1997
Experienced programmers tend to build a mental model of what goes on "underneath" the source code. Whether or not this model faithfully reflects the actual implementation is relatively unimportant as long as it can be used to predict the actions of a program correctly. However, when new language constructs or situations are encountered, the model often breaks down and needs to be augmented or replaced by a better one. (If this sounds like a description of progress in physics, you may not be far from right.) It is generally acknowledged that C++ is a complex programming language, with many fine points. Consequently, the model of the "C++ machine" is likely to be complex as well. Exactly how complex is the topic of Stan Lippman's Inside the C++ Object Model.
The purpose of Inside the C++ Object Model is to give an overview of the mechanisms that underlie the realization of the C++ object model. It does not deal explicitly with the way that the C++ language constructs can be used to implement object-oriented programs, although obviously, knowledge of the language internals may influence the design of a program if efficiency means anything. It also assumes a fair knowledge of C++ and a reasonable experience with programming in C++; if you lack these, you'll probably wonder what all the fuss is about.
The implementation of the C++ object model is shared between the compiler and the run-time support mechanisms. Much of the externally observable behavior of a C++ program is prescribed by the upcoming C++ standard, but not all; additionally, there are usually several ways to implement a given feature. As a consequence, different compiler vendors do things in slightly different ways, thereby greatly enhancing the confusion among programmers when a program that worked with one compiler breaks on the next. As a developer of the cfront C++ compiler, Lippman is in an excellent position to discuss both the similarities and the differences between implementations of the C++ object model, pointing out some common pitfalls for programmers along the way.
The book is divided into seven chapters, plus a preface that provides quite an interesting bit of C++ history. Chapter 1, "Object Lessons," is a gentle introduction to the general approach taken by C++ compilers to implement features such as data and function members, inheritance, and polymorphism. Chapter 2, "The Semantics of Constructors," treats the tortuous situations that C++ compilers must deal with to ensure that they synthesize correct constructors for objects that need them, or augment user-defined ones, preferably without generating too much object code in the process. Here, as in subsequent chapters, Lippman uses small-scale benchmark programs to assess the effects of using various C++ constructs. Incidentally, although these benchmarks involve only two C++ compilers, they show that the quality of the object code is often as much a function of the compiler as it is of the requirements of the C++ Object Model.
Chapter 3, "The Semantics of Data," deals with the layout of, and access to, data members in C++ objects under circumstances varying from a simple class, through single and multiple inheritance, to virtual base classes. Again, several benchmarks show the costs of different C++ features. Chapter 4, "The Semantics of Function," looks at the other C++ class members: the member functions, virtual or otherwise. This is one area where C++ compiler vendors have gone to particular trouble to cover all cases with reasonable efficiency, as Lippman makes clear. Chapter 5, "Semantics of Construction, Destruction, and Copy," continues where chapter 2 left off and considers the implementation support that governs the lifetime of C++ objects as they are created, copied, and destroyed, and ways by which smart compilers can optimize performance.
Up to this point, most of the discussion pertains to the way that a C++ compiler transforms a C++ program into an executable representation of the C++ object model during the compilation process. In chapter 6, "Runtime Semantics," the focus shifts to the actions that take place at run time: creation and destruction of globals, creation and destruction of temporaries (those anonymous objects created to hold intermediate values), and the operation of operators new and delete. Chapter 7, "On the Cusp of the Object Model," concludes with a discussion of the most-recent additions to C++: templates, exception handling, and run-time type identification, and the way these are treated by the compiler and the run-time support system.
What's it like to read this book? If you enjoy books such as Bjarne Stroustrup's The C++ Programming Language and The Design and Evolution of C++, you'll like this one. The style and assumed level of experience are similar; moreover, Inside the C++ Object Model covers ground that was left untouched by previous books. You'd probably do best to read it piecemeal in order to get the maximum benefit from it. If not, the issues that Lippman raises are likely to blend together. This last point is also one of my criticisms. While the organization of the book definitely makes sense (see the chapter titles mentioned previously), it causes some subjects to be spread across the book. However, because any printed book has an inherently linear organization, it's difficult to reconcile the requirements of depth-first and breadth-first treatments, and I think the order chosen here is certainly very reasonable. Two other minor points: The use of "Can you see why?"-style questions to the reader become a bit tedious after a while; and benchmark data from other C++ compilers is absent. (This would be useful because the data given in the book indicates important differences between the compilers, and it makes you wonder how representative the results are. Only for exception handling does Lippman give results from compilers other than those used throughout the remainder of the book.)
I recommend the book for anyone serious about C++ programming. In addition to the technical ins and outs of C++ implementations, it gives a good assessment of the actual costs (implementation-wise, that is) of programming in C++ as opposed to C, and will help you to choose the most efficient C++ constructs for a given purpose. Finally, the comparisons between the different approaches taken by C++ vendors will help you to avoid dependence on specific implementation characteristics, or at least give you a feeling of where to look for them. (Fortunately, C++ implementations seem to converge somewhat.) You won't envy C++ compiler writers much after reading this book, though.
-- Ron van der Wal
A plaintext message may be hidden in one of two ways. Steganography conceals the very existence of the message whereas cryptography renders the message unintelligible to outsiders by various transformations of the plaintext. The advantage of steganography is that it can be employed by parties who have something to lose should the fact of their secret communication (not just the content) be discovered. Encryption suggests that a message is important or secret, and may identify the sender or receiver as someone with something to hide.
Historically, various steganographic techniques have been used, including:
Although these techniques are archaic, they have contemporary equivalents. For example, the Kodak Photo CD format's maximum resolution is 2048×3072 pixels, with each pixel containing 24 bits of RGB information. The least significant bit of each 24-bit pixel can be changed without noticeably affecting the quality of the image. The result: You can hide a 2.3-MB message in a single digital snapshot.
This and many other modern steganographic techniques are presented in Peter Wayner's remarkable book, Disappearing Cryptography, which should be an eye opener to anyone who equates security with encryption. The book covers a wide range of techniques and also looks at the combined use of encryption and steganography and ways to exploit compression.
Wayner also covers the use of anonymous remailers. Again, the complexity of the issues involved in using anonymous remailers will come as a surprise to many who have made casual use of such services.
The presentation style is effective. Disappearing Cryptography devotes one chapter to each major topic. Each chapter begins with a light, allegorical exploration of the topic, intended to suggest the philosophy of the particular technique. This is followed by a general functional description of the technique that is easily accessible to the general reader. Finally, each chapter closes with a detailed technical treatment.
-- William Stallings
DDJ