home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / lang / cplus / 12974 < prev    next >
Encoding:
Text File  |  1992-08-27  |  6.3 KB  |  170 lines

  1. Path: sparky!uunet!iphasew!igor!thor!rmartin
  2. From: rmartin@thor.Rational.COM (Bob Martin)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: HELP NEEDED - INHERITING A LIST AND SORTING IT - C++
  5. Message-ID: <rmartin.714928075@thor>
  6. Date: 27 Aug 92 15:07:55 GMT
  7. References: <1992Aug26.123237.29846@monu6.cc.monash.edu.au>
  8. Sender: news@Rational.COM
  9. Lines: 159
  10.  
  11.  
  12. These are traditional problems with C++'s contravariant virtual functions.
  13. I have embedded a few suggestions below.
  14.  
  15. int376f@aurora.cc.monash.edu.au (Jamie Scuglia) writes:
  16.  
  17.  
  18. |Hi!  I have a small problem with some C++ code I am writing, to handle
  19. |a football tipping competion.  I have a ladder structure, which is to
  20. |hold ladders (positions) of participants in the tipping competition, as well
  21. |as a ladder of the football teams.  My basic class structure consists
  22. |of 3 classes for the ladder system.  
  23.  
  24. |- A base class, called LADDER, which defines the basic attributes
  25. |  of a ladder, including a definition for a simple linked list, and
  26. |  it also has a member function to sort the ladder.
  27.  
  28. |- A derived class called TEAM_LADDER which includes some more attributes
  29. |  related to football team statistics that a ladder holds.
  30.  
  31. |- A second derived class (from LADDER), called PLAYER_LADDER, which
  32. |  has a more specilized attributes relating to player performance in
  33. |  the tipping competition.
  34.  
  35. |The set-up of these classes looks like this: (with '....' = other stuff)
  36.  
  37. Note the changes that I have made to this code.  Specifically the
  38. inclusion of a pure virtual Compare function in LADDER, and the
  39. corresponding changes in the derived classes.
  40.  
  41.  
  42. |------------------------------------------------------------------------
  43. |class LADDER
  44. |{
  45. |   protected:
  46. |    char name[60];
  47. |    int points;
  48. |    float percentage;
  49. |    LADDER *next_entry;
  50. |    LADDER *last_entry; 
  51.  
  52. |   public:
  53. |    LADDER(char n[], int pts, float p, LADDER *next, LADDER* last);
  54.         virtual int Compare(const LADDER&) const = 0;
  55. |    virtual LADDER *Sort_ladder(LADDER *ladder);
  56. |};
  57.  
  58. |class PLAYER_LADDER : public LADDER
  59. |{    
  60. |   private:
  61. |    char login_name[12]; 
  62.  
  63. |   public:
  64. |    ........
  65. |    void Save_ladder(PLAYER_LADDER *ladder);
  66. |    int Compare(const LADDER&) const;
  67. |    .........
  68. |};
  69.  
  70. |class TEAM_LADDER : public LADDER
  71. |{
  72. |   private:
  73. |      float match_ratio;
  74. |      int games_played;
  75. |      ......
  76.  
  77. |   public:
  78. |      ......
  79. |      void Save_ladder(TEAM_LADDER *ladder);
  80. |      int Compare(const LADDER&) const;
  81. |      ......
  82. |};
  83. |------------------------------------------------------------------------
  84.  
  85. |Now, the problem occurs in the function PLAYER_LADDER:Save_ladder,
  86. |(and TEAM_LADDER::Save_ladder).
  87. |To save the ladder, I need to traverse the list I inherited from
  88. |the base class LADDER.  *BUT*, I can't do that (the compiler complains)
  89. |because in Save_ladder there is this code:
  90.  
  91. |        PLAYER_LADDER *player_ladder;
  92. |        ......
  93. |        player_ladder = player_ladder->next;  /* next ladder entry */
  94.  
  95. |which is no good, because the "next" pointer in PLAYER_LADDER actually
  96. |points to the base class LADDER, not PLAYER_LADDER as I wanted it to, 
  97. |even though I inherited the basic list structure from the class LADDER.
  98. |So, the compiler tells me it can't assign a type PLAYER_LADDER to LADDER
  99. |when I try to traverse the list that I inherited.  And I can't change the
  100. |"player_ladder" pointer to type LADDER (base-class) because then I would
  101. |lose the "login_name" field I added to PLAYER_LADDER.
  102.  
  103. Right.  This is contravariance, the types of virtual functions do not
  104. vary with the type of the class that contains them.  There are several
  105. ways around this, the simplest and most dangerous being to downcast:
  106.  
  107.     PLAYER_LADDER* pl;
  108.         ...
  109.         pl = (PLAYER_LADDER*)(pl->next);
  110.  
  111. This is dangerous because you are losing a little type safety.  You
  112. must make the assumption that the 'next' element will always really
  113. point to a PLAYER_LADDER...
  114.  
  115. There are better ways to address this problem.  Templates being one of
  116. them.  Templates do not suffer so much from contravariance, their
  117. arguments are covariant.  In an industrial environment I would opt for
  118. a template solution, or some other solution that preserved type
  119. safety.  But if your application is informal, then downcasting is
  120. probably sufficient...  
  121.  
  122.  
  123. |You also might notice that I have a "Compare" function in
  124. |PLAYER_LADDER and TEAM_LADDER classes to compare two ladder entries
  125. |to see if they are in the correct order.  Since a TEAM_LADDER and
  126. |a PLAYER_LADDER are sorted according to different criteria, I wrote
  127. |a version for each of these 2 classes.  Now, the function Sort_ladder
  128. |in the base class has an awful lot of trouble finding the correct Compare
  129. |function. 
  130.  
  131. Right.  Contravariance again. This is why I put the pure virtual
  132. Compare function into LADDER.  By sacrificing a little type safety you
  133. can write your Sort function so that it upcasts the PLAYER_LADDER or
  134. TEAM_LADDER pointers to LADDER poitners.  Then in each of the derived
  135. Compare function, you can downcast them again.  In this manner you can
  136. get a single Sort method in LADDER to do the sorting in both derived
  137. lists.
  138.  
  139. Again, there are better ways, and again using templates is one of
  140. them.
  141.  
  142. ------------------
  143.  
  144. Now let me digress just a bit.  The structure you have built is very
  145. common, and it possesses a common flaw.  You are mixing two
  146. abstractions into a single class.  The first abstraction is that of an
  147. element of a ladder.  The second abstraction is that of the ladder
  148. itself, i.e. the list of ladder elements preserved in some order.
  149.  
  150. I recommend, as a learning excersize, that you try to design this
  151. application using the two concepts rather than mixing them into a
  152. single class.  Create LADDER_ENTRY as an abstract base, and then
  153. derive PLAYER_LADDER_ENDTRY and TEAM_LADDER_ENTRY from it.  Also
  154. create a LADDER class to represent the ordered list.  You might need
  155. to derive PLAYER_LADDER and TEAM_LADDER from it, and you might not...
  156. Also, you might want to come up with a LADDER_ENTRY_LIST class which
  157. supports the ordered nature of LADDER_ENTRY objects, but does not
  158. encapsulate any of the other attributes of a LADDER.  The LADDER class
  159. would then contain at least one instance of such a list...
  160.  
  161.  
  162. Just some ideas....
  163.  
  164.  
  165. --
  166. Robert Martin                        Training courses offered in:
  167. R. C. M. Consulting                       Object Oriented Analysis
  168. 2080 Cranbrook Rd.                        Object Oriented Design
  169. Green Oaks, Il 60048 (708) 918-1004       C++
  170.