Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C for C++ Programmers


Dr. Dobb's Journal July 1997: C Programming

Al is a DDJ contributing editor. He can be contacted at [email protected].


After reading this column, you might be tempted to accuse me of exercising the Pournellian imperative by using my column to promote my own books. Yes, I am going to discuss some books that I wrote. No, the typical reader of this column, who already knows C and C++, does not need to buy or read any of these books. Therefore, these plugs -- er, discussions -- won't serve to increase my royalties by any significant amount. They do, however, lead to some discoveries about C and C++ that I will share with you.

It started early this spring when I began work on a fifth edition of an introductory C++ book called Teach Yourself C++. I've never liked that title, which the publisher chose for the first edition in 1990 after I rejected C++ for C Programmers, which Ira Pohl had already used. I always considered the title "Teach Yourself" to be only a notch higher on the schlock scale than something like Quantum Electrodynamics for Dummies. Someone must have liked it, however, because at least two other publishers have published books with the same dumb title. Title notwithstanding, the work has been professionally and financially rewarding, and I am proud of the content, particularly as it has changed and grown since the first edition. The fifth edition is, I think, the best.

If (Editions == 5) Why?

There are several reasons to upgrade a book that addresses technology -- the technology changes, the author's knowledge changes, the potential readership changes, or some combination thereof. All these reasons apply to this new edition of TYC++, and I'll discuss them and address some things about C++ that I discovered as I modified major parts of the book.

Writing a new edition of a programming language book means starting with the previous edition, going through it chapter by chapter, exercise by exercise, and reviewing what has changed in the language. There's probably no reason to revise a language book if the language hasn't changed, other than the usual publisher avarice and rapacity. (Don't worry. I'm not in any trouble. Publishers tend not to know words like that, and editors don't bother telling them.) The C++ draft standard has undergone only minor changes since the publication of the fourth edition of TYC++, but the C++ language being used has seen dramatic differences, primarily because more and more compilers now implement recent language inventions (also called innovations) of the ANSI committee. From my perspective, the so-called standard language is, at any given time, what is available and acceptable to programmers, rather than what is under deliberation. The final (we hope) draft standard document is still being considered, and no compiler has yet implemented the complete standard language as currently defined. Consequently, even though the fifth edition of TYC++ is still in the works, I have already agreed to write a sixth. Watch for it around Christmas of 1999, irrespective of the committee's status and the compilers' compliance. Whatever else happens, the C++ of late 1999 shall be the C++ of record as far as this author is concerned. At least that's what I'm saying now.

Changes in my understanding of the language and how it ought to be used influenced the fifth edition, too. Over the years, the book's content and my knowledge of C++ have been affected by the comments of readers and C++ experts. Greg Comeau of Comeau Computing, who knows a lot more about C++ than most of us, generously spent some time and marked up a copy of an early edition to correct some errors. John Dlugosz offered suggestions in areas where my code worked with contemporary compilers but did not conform to acknowledged C++ conventions or proposed standards. Those are only two of the many programmers who helped me. Since 1990, I have read many books on C++ and learned from most of them. My own programming experience on several different platforms -- DOS, Windows 95, Windows NT, OS/2, UNIX -- has, over the years, changed my views of how C++ code ought to be written. All these things and all these people change what I understand and, consequently, what I write.

Finally, changes in the kinds of programmers who need a book like this -- its targeted audience -- affect its content. All the previous editions, like most introductory books on C++, assume that the reader is a C programmer. I wrote a companion book, called Al Stevens Teaches C (another publisher-suggested title that I came to hate), for programmers who do not yet know C.

Since then, I have discovered two things. First, the C component of C++ has significant differences from ANSI C. (Not necessarily news when regarded in fragments, but substantial when viewed as a whole.) Some of these differences are found in the language itself; others are reflected in how C++ programmers use the language. Second, most C programmers have already made the move to C++. There is not nearly as much need for a C++ book that assumes a C programmer as there used to be. Potential readers of an introductory C++ book today are programmers who know neither language.

Consequently, I decided to adapt some of the C book's contents into the first several chapters of the C++ book, turning TYC++ into a book that covers the subject wall to wall. And that's where I made some interesting discoveries about C from a C++ perspective. I think that most programmers who ascended from C into C++ know and understand these things, but viewing them as a group provides a dramatic insight into how C++ has influenced its C component. And viewing them from the surrogate perspective of someone who knows neither language and is learning C++, which the author of such a tutorial must do, is a revealing exercise. I offer these observations, conclusions, and opinions here for anyone who sets out to teach C++ with or without my book.

C++[C]==--C;

First off, there is a lot about C that you might not need to teach. For starters, the whole stdio family of functions, although included in C++, has become unnecessary, having been replaced by the C++ stream classes. What's the point of teaching two ways to do something when the contemporary way is clearly superior? Remember, you are teaching C++ -- not C -- to a programmer. It's sufficient to refer those who are interested in ANSI C to an ANSI C textbook. Why dwell on malloc, calloc, and free when the C++ new and delete operators do a better job? You can bypass teaching setjmp and longjmp, too, because C++ exception handling does the same thing only better. You don't have to mention NULL because C++ programmers tend not to use it. Certainly, you can leave out the old-style C function parameter declarators. C++ compilers don't support them. You don't have to explain the need for void in a prototype's empty parameter list, because C++, although it permits a void parameter list, does not need the void keyword to distinguish a prototype from an old-style C function declaration; C++ does not support the classic K&R C function declaration. C typecasts are old hat now that we have C++ new-style casting. The string.h functions are candidates for the junk pile, being replaced by the more intuitive C++ string class.

After much thought, I decided to teach some of the obsolete (from a C++ programmer's perspective) C idioms and briefly touch on the Standard C library's string, memory allocation, and setjmp functions if only because a C++ programmer is likely to run into them. Sometimes the C string functions (strcpy, strcmp, and the like) are more efficient than the C++ string class, which can make a difference in a time-critical application. I found setjmp and longjmp particularly useful as a precursor to C++ exception handling because they do explicitly what the C++ try, catch, and throw operators do under the surface. So, this particular obsolete C behavior turns out to have a lingering use after all. I also decided to address C casts during an exercise early in the book. The exercise needed a cast and the book was nowhere near an advanced-enough stage to teach the appropriate new-style cast. Finally, if you are going to teach a programmer to overload global new and delete, it's easier if you start by teaching malloc and free.

CPP = Forever

You cannot, however, ignore the preprocessor, even though many C++ luminaries would like to see the preprocessor quietly become extinct. Inline functions are an improved way to write macros with parameters, and const variables replace #defined global symbols, but these improvements do not totally replace macros, particularly when certain macro constructs provide mnemonic expressions that expand into complex initialized data structures. Prominent examples are the macros that GUI application-framework class libraries use to manage things such as message maps. And consider the trace macro

#define trace(p) cerr << #p " = " << p << endl

that I use to avoid launching the cantankerous gdb debugger. You can't do that with inline functions. You must teach the preprocessor because, sooner or later, most C++ programmers need it.

If (C++ > C) ...

Most introductory C++ books include chapters somewhere near the beginning that discuss the differences between C and C++ with respect to notational improvements that C++ makes. Examples of these are the scope resolution operator, default function arguments, unnamed function parameters, the fact that function prototypes are required, variable declaration placement, inline functions, anonymous unions, variable declaration inside of for-loop and if-condition expressions, double-slash (//) comments, const instead of #define, structs as types, references, function overloading, and the signed type specifier.

An introductory C++ course that does not assume a student's knowledge of C presents a quandary. These subjects are best addressed in the C++ context in which they occur rather than as C improvements. The explanations of, for example, unnamed function parameters and default function arguments should accompany the discussions on function parameters and arguments -- as an integral part of the C++ language rather than as an extension to C. So what's the quandary? Just this. The student who, coincidentally, happens to already know C, may be tempted to sleep through -- or, in the case of a book, skip over -- the function parameter/argument discussion and, by doing so, misses learning about the improvements. What to do? You have no choice but to tell them they have to stay awake and alert through it all. But a student can lose interest wading through so much familiar material in wait of that occasional gem of new information.

Here are some more subtle differences that a C programmer might miss by skipping over the basics.

  • A void pointer in C++ cannot be assigned to a pointer to a type without a cast.
  • The C++ main function cannot be called recursively in a C++ program. C has no such restriction, although it probably should.
  • You must initialize a const object in C++.
  • Variables cannot be implicitly declared as int.
  • An enumerator is a type, not an int.
  • wchar_t and bool are real types, not typedefs.
  • floats are not always promoted to doubles in expressions.

As an author, I'm not sure I can solve the problem of a reader's attention span in such a book -- short of putting those eye-catching, tacky Dummies-like icons in the margins: "Stop!" "Look here!" "Important!" "New stuff!" "Wake up!" Such a practice would ruin my book's chances of becoming a respected textbook in the hallowed halls of higher learning. Wait a minute! What am I saying? What faculty in its right mind would adopt into its curriculum a textbook titled Teach Yourself anything? The students and the administration would ask the teachers, "What do we need you for?" Now I know why I don't like that title.

Believe it or not, the much maligned goto flow-control statement works quite differently in C++ than it does in C. You might wonder why they bother fixing goto when everyone tells everyone else not to use it. But goto, as permitted in C, would have a dangerous side effect in C++. Consider Example 1, which is valid C code and invalid C++ code. C++ does not permit goto to jump around the initialization of an automatic object, which could bypass a necessary constructor.

Class == Structure ? Not : Almost

C++ defines structures and classes almost identically. The only differences are related to access specifiers and inherited access. The members of a structure have public access by default. The members of a class have private access by default. Classes derived from a structure are derived publicly by default. Classes derived from a class are derived privately by default. If you didn't already know that, then go buy my book. Please?

This is not a difficult concept to explain when you are teaching C++ to a C programmer. You can ignore structs for a while and teach the behavior of classes. When the student understands classes, you can reveal the surprise that structs and classes are almost identical with the only differences being those I just explained. The C programmer has no problem with that. In fact, many C programmers are never taught that structures and classes are alike, and they program for years believing that C++ structs are just like C structs with no member functions, access specifiers, or participation in inheritance.

But when you are teaching C++ from scratch, the question that begs to be answered is this: Why have two different constructs when the differences are so small? I suppose you could ignore structs, but that would bypass necessary information; the programmer is bound to encounter them in C++ programs written by others. The only way to understand the reasons for the two is to understand that C++, as it strives for compatibility, inherits a lot of language from C. Which means that you must teach not only the origins and history of C++, but the consequences of all that legacy baggage, too.

The answer to the class versus struct question goes back to when the C++ class was evolving from the C struct. There is no real technical reason why the C++ struct should include all the properties of the class. C++ cannot eliminate the struct altogether; it must support the C struct to preserve compatibility between the languages according to Bjarne Stroustrup's original objectives for the C++ language. But without an explanation from Stroustrup, it is not clear why the structure needs member functions, access specifiers, and the ability to participate in class hierarchies. In The Design and Evolution of C++, (Addison-Wesley, 1994), Stroustrup says:

My intent was to have a single concept: a single set of layout rules, a single set of lookup rules, a single set of resolution rules, etc....Only a single concept would support my ideas of a smooth and gradual transition from "traditional C-style programming," through data abstraction, to object-oriented programming.

So, the reason is cultural rather than technical. Stroustrup goes on to explain that keeping the class and struct the same forestalled an otherwise unavoidable tendency on the part of language standardizers and specifiers to overwhelm the class specification with excess features while leaving the struct to implement only those features that involve low overhead and simplicity.

Given all that, when should you use a struct rather than a class? Or should you ever? Many programmers adopt this rule:

When the data structure's implementation is the same as its interface, use a struct. Otherwise use a class.

What are the implications of this rule? What does it mean when you say that the implementation is the interface? I developed the strategy several years ago for my own programs, but I never heard it expressed so well. I got the phrase from Ruminations on C++ (Addison-Wesley, 1997), by Andrew Koenig and Barbara Moo. This is a nice book with cover art that hides some surprises. At first it looks like the cover for a paperback edition of The Yearling or Huckleberry Finn. Then, because of the black and white cows, the book looks like a Gateway 2000 manual. Why the cows, I wondered. Then it hit me -- one of the authors is named "Moo." Then I remembered that "ruminating" means "chewing cud" as well as thinking about stuff. The book is an edited collection of Koenig's columns in various publications related to C++. Although some of his ruminations are dated (Old cud?), reflecting their age and the technology as it existed when he wrote them, this book is well worth a careful read. I highly recommend it.

Saying that a struct's implementation is its interface means simply the struct is typical C with data members only (no member functions), all of which have public access so that the format of the data members is what the application program views.

Object-oriented purists would contend that by following that rule, there is no valid application for the C++ struct. If you tend to fall into that camp, then C++ supports your beliefs. Use classes exclusively. If you lean more toward the pragmatic approach, then C++ permits you to write code by using either idiom. C++ has something for everyone.

C = C > C++ ? C : C++

C++ is sometimes called a superset of C, which makes C a subset of C++. Except that Standard C came first. C++ has been called an object-oriented extension of C, but that doesn't seem to work either because, as I learned, the C that C++ supports is not the C that Standard C compilers support. If you can forget about the chronology, perhaps the best description of the relationship between the two languages can be expressed like this: If you are comparing Standard C to C++, they are two different languages. The C component of C++ and the programming models that it supports are different enough from Standard C to set them apart. If you are addressing the C component that C++ compilers support, perhaps the best way to describe it is as a dialect of C++, one that enjoys certain notational improvements over Standard C and leaves out the data abstraction and object-oriented idioms. Stroustrup's "better C." C as a dialect of C++. What a peculiar notion!

DDJ


Copyright © 1997, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.