home *** CD-ROM | disk | FTP | other *** search
/ ftp.pasteur.org/FAQ/ / ftp-pasteur-org-FAQ.zip / FAQ / C++-faq / part5 < prev    next >
Encoding:
Text File  |  2000-03-01  |  19.7 KB  |  523 lines

  1. Path: senator-bedfellow.mit.edu!bloom-beacon.mit.edu!news-out.cwix.com!newsfeed.cwix.com!newsfeed.berkeley.edu!newsfeed.stanford.edu!novia!nntp3.cerf.net!nntp2.cerf.net!news.cerf.net!not-for-mail
  2. From: mpcline@nic.cerf.net (Marshall Cline)
  3. Newsgroups: comp.lang.c++,comp.answers,news.answers,alt.comp.lang.learn.c-c++
  4. Subject: C++ FAQ (part 5 of 10)
  5. Followup-To: comp.lang.c++
  6. Date: 29 Feb 2000 20:06:53 GMT
  7. Organization: ATT Cerfnet
  8. Lines: 502
  9. Approved: news-answers-request@mit.edu
  10. Distribution: world
  11. Expires: +1 month
  12. Message-ID: <89h8st$btu$1@news.cerf.net>
  13. Reply-To: cline@parashift.com (Marshall Cline)
  14. NNTP-Posting-Host: nic1.san.cerf.net
  15. X-Trace: news.cerf.net 951854813 12222 192.215.81.88 (29 Feb 2000 20:06:53 GMT)
  16. X-Complaints-To: abuse@cerf.net
  17. NNTP-Posting-Date: 29 Feb 2000 20:06:53 GMT
  18. Summary: Please read this before posting to comp.lang.c++
  19. Xref: senator-bedfellow.mit.edu comp.lang.c++:453816 comp.answers:39856 news.answers:178215 alt.comp.lang.learn.c-c++:40794
  20.  
  21. Archive-name: C++-faq/part5
  22. Posting-Frequency: monthly
  23. Last-modified: Feb 29, 2000
  24. URL: http://marshall-cline.home.att.net/cpp-faq-lite/
  25.  
  26. AUTHOR: Marshall Cline / cline@parashift.com / 972-931-9470
  27.  
  28. COPYRIGHT: This posting is part of "C++ FAQ Lite."  The entire "C++ FAQ Lite"
  29. document is Copyright(C)1991-2000 Marshall Cline, Ph.D., cline@parashift.com.
  30. All rights reserved.  Copying is permitted only under designated situations.
  31. For details, see section [1].
  32.  
  33. NO WARRANTY: THIS WORK IS PROVIDED ON AN "AS IS" BASIS.  THE AUTHOR PROVIDES NO
  34. WARRANTY WHATSOEVER, EITHER EXPRESS OR IMPLIED, REGARDING THE WORK, INCLUDING
  35. WARRANTIES WITH RESPECT TO ITS MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
  36. PURPOSE.
  37.  
  38. C++-FAQ-Lite != C++-FAQ-Book: This document, C++ FAQ Lite, is not the same as
  39. the C++ FAQ Book.  The book (C++ FAQs, Cline and Lomow, Addison-Wesley) is 500%
  40. larger than this document, and is available in bookstores.  For details, see
  41. section [3].
  42.  
  43. ==============================================================================
  44.  
  45. SECTION [14]: Friends
  46.  
  47.  
  48. [14.1] What is a friend?
  49.  
  50. Something to allow your class to grant access to another class or function.
  51.  
  52. Friends can be either functions or other classes.  A class grants access
  53. privileges to its friends.  Normally a developer has political and technical
  54. control over both the friend and member functions of a class (else you may need
  55. to get permission from the owner of the other pieces when you want to update
  56. your own class).
  57.  
  58. ==============================================================================
  59.  
  60. [14.2] Do friends violate encapsulation?
  61.  
  62. If they're used properly, they actually enhance encapsulation.
  63.  
  64. You often need to split a class in half when the two halves will have different
  65. numbers of instances or different lifetimes.  In these cases, the two halves
  66. usually need direct access to each other (the two halves used to be in the same
  67. class, so you haven't increased the amount of code that needs direct access to
  68. a data structure; you've simply reshuffled the code into two classes instead of
  69. one).  The safest way to implement this is to make the two halves friends of
  70. each other.
  71.  
  72. If you use friends like just described, you'll keep private things private.
  73. People who don't understand this often make naive efforts to avoid using
  74. friendship in situations like the above, and often they actually destroy
  75. encapsulation.  They either use public data (grotesque!), or they make the data
  76. accessible between the halves via public get() and set() member functions.
  77. Having a public get() and set() member function for a private datum is OK only
  78. when the private datum "makes sense" from outside the class (from a user's
  79. perspective).  In many cases, these get()/set() member functions are almost as
  80. bad as public data: they hide (only) the name of the private datum, but they
  81. don't hide the existence of the private datum.
  82.  
  83. Similarly, if you use friend functions as a syntactic variant of a class's
  84. public: access functions, they don't violate encapsulation any more than a
  85. member function violates encapsulation.  In other words, a class's friends
  86. don't violate the encapsulation barrier: along with the class's member
  87. functions, they are the encapsulation barrier.
  88.  
  89. ==============================================================================
  90.  
  91. [14.3] What are some advantages/disadvantages of using friend functions?
  92.  
  93. They provide a degree of freedom in the interface design options.
  94.  
  95. Member functions and friend functions are equally privileged (100% vested).
  96. The major difference is that a friend function is called like f(x), while a
  97. member function is called like x.f().  Thus the ability to choose between
  98. member functions (x.f()) and friend functions (f(x)) allows a designer to
  99. select the syntax that is deemed most readable, which lowers maintenance costs.
  100.  
  101. The major disadvantage of friend functions is that they require an extra line
  102. of code when you want dynamic binding.  To get the effect of a virtual friend,
  103. the friend function should call a hidden (usually protected:) virtual[20]
  104. member function.  This is called the Virtual Friend Function Idiom[15.8].  For
  105. example:
  106.  
  107.     class Base {
  108.     public:
  109.       friend void f(Base& b);
  110.       // ...
  111.     protected:
  112.       virtual void do_f();
  113.       // ...
  114.     };
  115.  
  116.     inline void f(Base& b)
  117.     {
  118.       b.do_f();
  119.     }
  120.  
  121.     class Derived : public Base {
  122.     public:
  123.       // ...
  124.     protected:
  125.       virtual void do_f();  // "Override" the behavior of f(Base& b)
  126.       // ...
  127.     };
  128.  
  129.     void userCode(Base& b)
  130.     {
  131.       f(b);
  132.     }
  133.  
  134. The statement f(b) in userCode(Base&) will invoke b.do_f(), which is
  135. virtual[20].  This means that Derived::do_f() will get control if b is actually
  136. a object of class Derived.  Note that Derived overrides the behavior of the
  137. protected: virtual[20] member function do_f(); it does not have its own
  138. variation of the friend function, f(Base&).
  139.  
  140. ==============================================================================
  141.  
  142. [14.4] What does it mean that "friendship is neither inherited nor transitive"?
  143.  
  144. I may declare you as my friend, but that doesn't mean I necessarily trust
  145. either your kids or your friends.
  146.  * I don't necessarily trust the kids of my friends.  The privileges of
  147.    friendship aren't inherited.  Derived classes of a friend aren't necessarily
  148.    friends.  If class Fred declares that class Base is a friend, classes
  149.    derived from Base don't have any automatic special access rights to Fred
  150.    objects.
  151.  * I don't necessarily trust the friends of my friends.  The privileges of
  152.    friendship aren't transitive.  A friend of a friend isn't necessarily a
  153.    friend.  If class Fred declares class Wilma as a friend, and class Wilma
  154.    declares class Betty as a friend, class Betty doesn't necessarily have any
  155.    special access rights to Fred objects.
  156.  
  157. ==============================================================================
  158.  
  159. [14.5] Should my class declare a member function or a friend function?
  160.  
  161. Use a member when you can, and a friend when you have to.
  162.  
  163. Sometimes friends are syntactically better (e.g., in class Fred, friend
  164. functions allow the Fred parameter to be second, while members require it to be
  165. first).  Another good use of friend functions are the binary infix arithmetic
  166. operators.  E.g., aComplex + aComplex should be defined as a friend rather than
  167. a member if you want to allow aFloat + aComplex as well (member functions don't
  168. allow promotion of the left hand argument, since that would change the class of
  169. the object that is the recipient of the member function invocation).
  170.  
  171. In other cases, choose a member function over a friend function.
  172.  
  173. ==============================================================================
  174.  
  175. SECTION [15]: Input/output via <iostream.h> and <stdio.h>
  176.  
  177.  
  178. [15.1] Why should I use <iostream.h> instead of the traditional <stdio.h>?
  179.  
  180. Increase type safety, reduce errors, improve performance, allow extensibility,
  181. and provide subclassability.
  182.  
  183. printf() is arguably not broken, and scanf() is perhaps livable despite being
  184. error prone, however both are limited with respect to what C++ I/O can do.  C++
  185. I/O (using << and >>) is, relative to C (using printf() and scanf()):
  186.  * Better type safety: With <iostream.h>, the type of object being I/O'd is
  187.    known statically by the compiler.  In contrast, <stdio.h> uses "%" fields to
  188.    figure out the types dynamically.
  189.  * Less error prone: With <iostream.h>, there are no redundant "%" tokens that
  190.    have to be consistent with the actual objects being I/O'd.  Removing
  191.    redundancy removes a class of errors.
  192.  * Extensible: The C++ <iostream.h> mechanism allows new user-defined types to
  193.    be I/O'd without breaking existing code.  Imagine the chaos if everyone was
  194.    simultaneously adding new incompatible "%" fields to printf() and
  195.    scanf()?!).
  196.  * Subclassable: The C++ <iostream.h> mechanism is built from real classes such
  197.    as ostream and istream.  Unlike <stdio.h>'s FILE*, these are real classes
  198.    and hence subclassable.  This means you can have other user-defined things
  199.    that look and act like streams, yet that do whatever strange and wonderful
  200.    things you want.  You automatically get to use the zillions of lines of I/O
  201.    code written by users you don't even know, and they don't need to know about
  202.    your "extended stream" class.
  203.  
  204. ==============================================================================
  205.  
  206. [15.2] Why does my program go into an infinite loop when someone enters an
  207.        invalid input character?
  208.  
  209. For example, suppose you have the following code that reads integers from cin:
  210.  
  211.     #include <iostream.h>
  212.  
  213.     int main()
  214.     {
  215.       cout << "Enter numbers separated by whitespace (use -1 to quit): ";
  216.       int i = 0;
  217.       while (i != -1) {
  218.         cin >> i;        // BAD FORM -- See comments below
  219.         cout << "You entered " << i << '\n';
  220.       }
  221.     }
  222.  
  223. The problem with this code is that it lacks any checking to see if someone
  224. entered an invalid input character.  In particular, if someone enters something
  225. that doesn't look like an integer (such as an 'x'), the stream cin goes into a
  226. "failed state," and all subsequent input attempts return immediately without
  227. doing anything.  In other words, the program enters an infinite loop; if 42 was
  228. the last number that was successfully read, the program will print the message
  229. You entered 42 over and over.
  230.  
  231. An easy way to check for invalid input is to move the input request from the
  232. body of the while loop into the control-expression of the while loop.  E.g.,
  233.  
  234.     #include <iostream.h>
  235.  
  236.     int main()
  237.     {
  238.       cout << "Enter a number, or -1 to quit: ";
  239.       int i = 0;
  240.       while (cin >> i) {    // GOOD FORM
  241.         if (i == -1) break;
  242.         cout << "You entered " << i << '\n';
  243.       }
  244.     }
  245.  
  246. This will cause the while loop to exit either when you hit end-of-file, or when
  247. you enter a bad integer, or when you enter -1.
  248.  
  249. (Naturally you can eliminate the break by changing the while loop expression
  250. from while (cin >> i) to while ((cin >> i) && (i != -1)), but that's not really
  251. the point of this FAQ since this FAQ has to do with iostreams rather than
  252. generic structured programming guidelines.)
  253.  
  254. ==============================================================================
  255.  
  256. [15.3] How does that funky while (cin >> foo) syntax work?
  257.  
  258. See the previous FAQ[15.2] for an example of the "funky while (cin >> foo)
  259. syntax."
  260.  
  261. The expression (cin >> foo) calls the appropriate operator>> (for example, it
  262. calls the operator>> that takes an istream on the left and, if foo is of type
  263. int, an int& on the right).  The istream operator>> functions return their left
  264. argument by convention, which in this case means it will return cin.  Next the
  265. compiler notices that the returned istream is in a boolean context, so it
  266. converts that istream into a boolean.
  267.  
  268. To convert an istream into a boolean, the compiler calls a member function
  269. called istream::operator void*().  This returns a void* pointer, which is in
  270. turn converted to a boolean (NULL becomes false, any other pointer becomes
  271. true).  So in this case the compiler generates a call to cin.operator void*(),
  272. just as if you had casted it explicitly such as (void*)cin.
  273.  
  274. The operator void*() cast operator returns some non-NULL pointer if the stream
  275. is in a good state, or NULL if it's in a failed state.  For example, if you
  276. read one too many times (e.g., if you're already at end-of-file), or if the
  277. actual info on the input stream isn't valid for the type of foo (e.g., if foo
  278. is an int and the data is an 'x' character), the stream will go into a failed
  279. state and the cast operator will return NULL.
  280.  
  281. The reason operator>> doesn't simply return a bool (or void*) indicating
  282. whether it succeeded or failed is to support the "cascading" syntax:
  283.  
  284.       cin >> foo >> bar;
  285.  
  286. The operator>> is left-associative, which means the above is parsed as:
  287.  
  288.       (cin >> foo) >> bar;
  289.  
  290. In other words, if we replace operator>> with a normal function name such as
  291. readFrom(), this becomes the expression:
  292.  
  293.       readFrom( readFrom(cin, foo), bar);
  294.  
  295. As always, we begin evaluating at the innermost expression.  Because of the
  296. left-associativity of operator>>, this happens to be the left-most expression,
  297. cin >> foo.  This expression returns cin (more precisely, it returns a
  298. reference to its left-hand argument) to the next expression.  The next
  299. expression also returns (a reference to) cin, but this second reference is
  300. ignored since it's the outermost expression in this "expression statement."
  301.  
  302. ==============================================================================
  303.  
  304. [15.4] Why does my input seem to process past the end of file?
  305.  
  306. Because the eof state may not get set until after a read is attempted past the
  307. end of file.  That is, reading the last byte from a file might not set the eof
  308. state.  E.g., suppose the input stream is mapped to a keyboard -- in that case
  309. it's not even theoretically possible for the C++ library to predict whether or
  310. not the character that the user just typed will be the last character.
  311.  
  312. For example, the following code might have an off-by-one error with the count
  313. i:
  314.  
  315.     int i = 0;
  316.     while (! cin.eof()) {   // WRONG! (not reliable)
  317.       cin >> x;
  318.       ++i;
  319.       // Work with x ...
  320.     }
  321.  
  322. What you really need is:
  323.  
  324.     int i = 0;
  325.     while (cin >> x) {      // RIGHT! (reliable)
  326.       ++i;
  327.       // Work with x ...
  328.     }
  329.  
  330. ==============================================================================
  331.  
  332. [15.5] Why is my program ignoring my input request after the first iteration?
  333.  
  334. Because the numerical extractor leaves non-digits behind in the input buffer.
  335.  
  336. If your code looks like this:
  337.  
  338.     char name[1000];
  339.     int age;
  340.  
  341.     for (;;) {
  342.       cout << "Name: ";
  343.       cin >> name;
  344.       cout << "Age: ";
  345.       cin >> age;
  346.     }
  347.  
  348. What you really want is:
  349.  
  350.     for (;;) {
  351.       cout << "Name: ";
  352.       cin >> name;
  353.       cout << "Age: ";
  354.       cin >> age;
  355.       cin.ignore(INT_MAX, '\n');
  356.     }
  357.  
  358. ==============================================================================
  359.  
  360. [15.6] How can I provide printing for my class Fred?
  361.  
  362. Use operator overloading[13] to provide a friend[14] left-shift operator,
  363. operator<<.
  364.  
  365.     #include <iostream.h>
  366.  
  367.     class Fred {
  368.     public:
  369.       friend ostream& operator<< (ostream& o, const Fred& fred);
  370.       // ...
  371.     private:
  372.       int i_;    // Just for illustration
  373.     };
  374.  
  375.     ostream& operator<< (ostream& o, const Fred& fred)
  376.     {
  377.       return o << fred.i_;
  378.     }
  379.  
  380.     int main()
  381.     {
  382.       Fred f;
  383.       cout << "My Fred object: " << f << "\n";
  384.     }
  385.  
  386. We use a non-member function (a friend[14] in this case) since the Fred object
  387. is the right-hand operand of the << operator.  If the Fred object was supposed
  388. to be on the left hand side of the << (that is, myFred << cout rather than
  389. cout << myFred), we could have used a member function named operator<<.
  390.  
  391. Note that operator<< returns the stream.  This is so the output operations can
  392. be cascaded[15.3].
  393.  
  394. ==============================================================================
  395.  
  396. [15.7] How can I provide input for my class Fred?
  397.  
  398. Use operator overloading[13] to provide a friend[14] right-shift operator,
  399. operator>>.  This is similar to the output operator[15.6], except the parameter
  400. doesn't have a const[18]: "Fred&" rather than "const Fred&".
  401.  
  402.     #include <iostream.h>
  403.  
  404.     class Fred {
  405.     public:
  406.       friend istream& operator>> (istream& i, Fred& fred);
  407.       // ...
  408.     private:
  409.       int i_;    // Just for illustration
  410.     };
  411.  
  412.     istream& operator>> (istream& i, Fred& fred)
  413.     {
  414.       return i >> fred.i_;
  415.     }
  416.  
  417.     int main()
  418.     {
  419.       Fred f;
  420.       cout << "Enter a Fred object: ";
  421.       cin >> f;
  422.       // ...
  423.     }
  424.  
  425. Note that operator>> returns the stream.  This is so the input operations can
  426. be cascaded and/or used in a while loop or if statement[15.3].
  427.  
  428. ==============================================================================
  429.  
  430. [15.8] How can I provide printing for an entire hierarchy of classes?
  431.  
  432. Provide a friend[14] operator<<[15.6] that calls a protected virtual[20]
  433. function:
  434.  
  435.     class Base {
  436.     public:
  437.       friend ostream& operator<< (ostream& o, const Base& b);
  438.       // ...
  439.     protected:
  440.       virtual void print(ostream& o) const;
  441.     };
  442.  
  443.     inline ostream& operator<< (ostream& o, const Base& b)
  444.     {
  445.       b.print(o);
  446.       return o;
  447.     }
  448.  
  449.     class Derived : public Base {
  450.     protected:
  451.       virtual void print(ostream& o) const;
  452.     };
  453.  
  454. The end result is that operator<< acts as if it was dynamically bound, even
  455. though it's a friend[14] function.  This is called the Virtual Friend Function
  456. Idiom.
  457.  
  458. Note that derived classes override print(ostream&) const.  In particular, they
  459. do not provide their own operator<<.
  460.  
  461. Naturally if Base is an ABC[22.3], Base::print(ostream&) const can be declared
  462. pure virtual[22.4] using the "= 0" syntax.
  463.  
  464. ==============================================================================
  465.  
  466. [15.9] How can I "reopen" cin and cout in binary mode under DOS and/or OS/2?
  467.  
  468. This is implementation dependent.  Check with your compiler's documentation.
  469.  
  470. For example, suppose you want to do binary I/O using cin and cout.  Suppose
  471. further that your operating system (such as DOS or OS/2) insists on translating
  472. "\r\n" into "\n" on input from cin, and "\n" to "\r\n" on output to cout or
  473. cerr.
  474.  
  475. Unfortunately there is no standard way to cause cin, cout, and/or cerr to be
  476. opened in binary mode.  Closing the streams and attempting to reopen them in
  477. binary mode might have unexpected or undesirable results.
  478.  
  479. On systems where it makes a difference, the implementation might provide a way
  480. to make them binary streams, but you would have to check the manuals to find
  481. out.
  482.  
  483. ==============================================================================
  484.  
  485. [15.10] Why can't I open a file in a different directory such as "..\test.dat"?
  486.         [UPDATED!]
  487.  
  488. [Recently added an explanation that the library routines treat "/" and "\"
  489. interchangeably thanks to Stan Brown (on 1/00).]
  490.  
  491. Because "\t" is a tab character.
  492.  
  493. You should use forward slashes in your filenames, even on an operating system
  494. that uses backslashes such as DOS, Windows, OS/2, etc.  For example:
  495.  
  496.     #include <iostream.h>
  497.     #include <fstream.h>
  498.  
  499.     int main()
  500.     {
  501.       #if 1
  502.         ifstsream file("../test.dat");     // RIGHT!
  503.       #else
  504.         ifstsream file("..\test.dat");     // WRONG!
  505.       #endif
  506.  
  507.       // ...
  508.     }
  509.  
  510. Remember, the backslash ("\") is used in string literals to create special
  511. characters: "\n" is a newline, "\b" is a backspace, and "\t" is a tab, "\a" is
  512. an "alert", "\v" is a vertical-tab, etc.  Therefore the file name
  513. "\version\next\alpha\beta\test.dat" is interpreted as a bunch of very funny
  514. characters; use "/version/next/alpha/beta/test.dat" instead, even on systems
  515. that use a "\" as the directory separator such as DOS, Windows, OS/2, etc.
  516. This is because the library routines on these operating systems handle "/" and
  517. "\" interchangeably.
  518.  
  519. ==============================================================================
  520.  
  521. -- 
  522. Marshall Cline / 972-931-9470 / mailto:cline@parashift.com
  523.