|
1 | 1 | # <a name="main"></a>C++ Core Guidelines
|
2 | 2 |
|
3 |
| -July 19, 2016 |
| 3 | +July 20, 2016 |
4 | 4 |
|
5 | 5 | Editors:
|
6 | 6 |
|
@@ -3537,7 +3537,7 @@ The "helper functions" have no need for direct access to the representation of a
|
3537 | 3537 |
|
3538 | 3538 | ##### Note
|
3539 | 3539 |
|
3540 |
| -This rule becomes even better if C++17 gets "uniform function call." ??? |
| 3540 | +This rule becomes even better if C++ gets ["]uniform function call](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0251r0.pdf). |
3541 | 3541 |
|
3542 | 3542 | ##### Enforcement
|
3543 | 3543 |
|
@@ -3644,7 +3644,7 @@ Prefer the order `public` members before `protected` members before `private` me
|
3644 | 3644 |
|
3645 | 3645 | ##### Enforcement
|
3646 | 3646 |
|
3647 |
| -??? |
| 3647 | +Flag protected data. |
3648 | 3648 |
|
3649 | 3649 | ## <a name="SS-concrete"></a>C.concrete: Concrete types
|
3650 | 3650 |
|
@@ -6005,15 +6005,214 @@ Use `virtual` only when declaring a new virtual function. Use `override` only wh
|
6005 | 6005 |
|
6006 | 6006 | ##### Reason
|
6007 | 6007 |
|
6008 |
| - ??? Herb: I've become a non-fan of implementation inheritance -- seems most often an anti-pattern. Are there reasonable examples of it? |
| 6008 | +Implementation details in an interface makes the interface brittle; |
| 6009 | +that is, makes its users vulnerable to having to recompile after changes in the implementation. |
| 6010 | +Data in a base class increases the complexity of implementing the base and can lead to replication of code. |
| 6011 | + |
| 6012 | +##### Note |
| 6013 | + |
| 6014 | +Definition: |
| 6015 | + |
| 6016 | +* interface inheritance is the use of inheritance to separate users from implementations, |
| 6017 | +in particular to allow derived classes to be added and changed without affecting the users of base classes. |
| 6018 | +* implementation inheritance is the use of inheritance to simplify implementation of new facilities |
| 6019 | +by making useful operations available for implementers of related new operations (sometimes called "programming by difference"). |
| 6020 | + |
| 6021 | +A pure interface class is simply a set of pure virtual functions; see [I.25](#Ri-abstract). |
| 6022 | + |
| 6023 | +In early OOP (e.g., in the 1980s and 1990s), implementation inheritance and interface inheritance were often mixed |
| 6024 | +and bad habits die hard. |
| 6025 | +Even now, mixtures are not uncommon in old code bases and in old-style teaching material. |
| 6026 | + |
| 6027 | +The importance of keeping the two kinds of inheritance increases |
| 6028 | + |
| 6029 | +* with the size of a hierarchy (e.g., dozens of derived classes), |
| 6030 | +* with the length of time the hierarchy is used (e.g., decades), and |
| 6031 | +* with the number of distinct organizations in which a hierarchy is used |
| 6032 | +(e.g., it can be difficult to distribute an update to a base class) |
| 6033 | + |
| 6034 | + |
| 6035 | +##### Example, bad |
| 6036 | + |
| 6037 | + class Shape { // BAD, mixed interface and implementation |
| 6038 | + public: |
| 6039 | + Shape(); |
| 6040 | + Shape(Point ce = {0,0}, Color co = none): cent{ce}, col {co} { /* ... */} |
| 6041 | + |
| 6042 | + Point center() const { return cent; } |
| 6043 | + Color color() const { return col; } |
| 6044 | + |
| 6045 | + virtual void rotate(int) = 0; |
| 6046 | + virtual void move(Point p) { cent = p; redraw(); } |
| 6047 | + |
| 6048 | + virtual void redraw(); |
| 6049 | + |
| 6050 | + // ... |
| 6051 | + public: |
| 6052 | + Point cent; |
| 6053 | + Color col; |
| 6054 | + }; |
| 6055 | + |
| 6056 | + class Circle : public Shape { |
| 6057 | + public: |
| 6058 | + Circle(Point c, int r) :Shape{c}, rad{r} { /* ... */ } |
| 6059 | + |
| 6060 | + // ... |
| 6061 | + private: |
| 6062 | + int rad; |
| 6063 | + }; |
| 6064 | + |
| 6065 | + class Triangle : public Shape { |
| 6066 | + public: |
| 6067 | + Triangle(Point p1, Point p2, Point p3); // calculate center |
| 6068 | + // ... |
| 6069 | + }; |
| 6070 | + |
| 6071 | +Problems: |
| 6072 | + |
| 6073 | +* As the hierarchy grows and more data is adder to `Shape`, the constructors gets harder to write and maintain. |
| 6074 | +* Why calculate the center for the `Triangle`? we may never us it. |
| 6075 | +* Add a data member to `Shape` (e.g., drawing style or canvas) |
| 6076 | +and all derived classes and all users needs to be reviewed, possibly changes, and probably recompiled. |
| 6077 | + |
| 6078 | +The implementation of `Shape::move()` is an example of implementation inheritance: |
| 6079 | +we have defined 'move()` once and for all for all derived classes. |
| 6080 | +The more code there is in such base class member function implementations and the more data is shared by placing it in the base, |
| 6081 | +the more benefits we gain - and the less stable the hierarchy is. |
6009 | 6082 |
|
6010 | 6083 | ##### Example
|
6011 | 6084 |
|
6012 |
| - ??? |
| 6085 | +This Shape hierarchy can be rewritten using interface inheritance: |
| 6086 | + |
| 6087 | + class Shape { // pure interface |
| 6088 | + public: |
| 6089 | + virtual Point center() const =0; |
| 6090 | + virtual Color color() const =0; |
| 6091 | + |
| 6092 | + virtual void rotate(int) =0; |
| 6093 | + virtual void move(Point p) =0; |
| 6094 | + |
| 6095 | + virtual void redraw() =0; |
| 6096 | + |
| 6097 | + // ... |
| 6098 | + }; |
| 6099 | + |
| 6100 | +Note that a pure interface rarely have constructors: there is nothing to construct. |
| 6101 | + |
| 6102 | + class Circle : public Shape { |
| 6103 | + public: |
| 6104 | + Circle(Point c, int r, Color c) :cent{c}, rad{r}, col{c} { /* ... */ } |
| 6105 | + |
| 6106 | + Point center() const override { return cent; } |
| 6107 | + Color color() const override { return col; } |
| 6108 | + |
| 6109 | + // ... |
| 6110 | + private: |
| 6111 | + Point cent; |
| 6112 | + int rad; |
| 6113 | + Color col; |
| 6114 | + }; |
| 6115 | + |
| 6116 | +The interface is now less brittle, but there is more work in implementing the member functions. |
| 6117 | +For example, `center` has to be implemented by every class derived from `Shape`. |
| 6118 | + |
| 6119 | +##### Example, dual hierarchy |
| 6120 | + |
| 6121 | +How can we gain the benefit of the stable hierarchies from implementation hierarchies and the benefit of implementation reuse from implementation inheritance. |
| 6122 | +One popular technique is dual hierarchies. |
| 6123 | +There are many ways of implementing the ide of dual hierarchies; here, we use a multiple-inheritance variant. |
| 6124 | + |
| 6125 | +First we devise a hierarchy of interface classes: |
| 6126 | + |
| 6127 | + class Shape { // pure interface |
| 6128 | + public: |
| 6129 | + virtual Point center() const =0; |
| 6130 | + virtual Color color() const =0; |
| 6131 | + |
| 6132 | + virtual void rotate(int) =0; |
| 6133 | + virtual void move(Point p) =0; |
| 6134 | + |
| 6135 | + virtual void redraw() =0; |
| 6136 | + |
| 6137 | + // ... |
| 6138 | + }; |
| 6139 | + |
| 6140 | + class Circle : public Shape { // pure interface |
| 6141 | + public: |
| 6142 | + int radius() = 0; |
| 6143 | + // ... |
| 6144 | + }; |
| 6145 | + |
| 6146 | +To make this interface useful, we must provide its implementation classes (here, named equivalently, but in the `Impl` namespace): |
| 6147 | + |
| 6148 | + class Impl::Shape : public Shape { // implementation |
| 6149 | + public: |
| 6150 | + // constructors, destructor |
| 6151 | + // ... |
| 6152 | + virtual Point center() const { /* ... */ } |
| 6153 | + virtual Color color() const { /* ... */ } |
| 6154 | + |
| 6155 | + virtual void rotate(int) { /* ... */ } |
| 6156 | + virtual void move(Point p) { /* ... */ } |
| 6157 | + |
| 6158 | + virtual void redraw() { /* ... */ } |
| 6159 | + |
| 6160 | + // ... |
| 6161 | + }; |
| 6162 | + |
| 6163 | +Now `Shape` is a poor example of a class with an implementation, |
| 6164 | +but bare with us because this is just a simple example of a technique aimed at more complex hierrchies. |
| 6165 | + |
| 6166 | + class Impl::Circle : public Circle, public Impl::Shape { // implementation |
| 6167 | + publc: |
| 6168 | + // constructors, destructor |
| 6169 | + |
| 6170 | + int radius() { /* ... */ } |
| 6171 | + // ... |
| 6172 | + }; |
| 6173 | + |
| 6174 | +And we could extend the hierarchies by adding a Smiley class (:-)): |
| 6175 | + |
| 6176 | + class Smiley : public Circle { // pure interface |
| 6177 | + public: |
| 6178 | + // ... |
| 6179 | + }; |
| 6180 | + |
| 6181 | + class Impl::Smiley : Public Smiley, public Impl::Circle { // implementation |
| 6182 | + publc: |
| 6183 | + // constructors, destructor |
| 6184 | + // ... |
| 6185 | + } |
| 6186 | + |
| 6187 | +There are now two hierarchies: |
| 6188 | + |
| 6189 | +* interface: Smiley -> Circle -> Shape |
| 6190 | +* implementation: Impl::Smiley -> Impl::Circle -> Impl::Shape |
| 6191 | + |
| 6192 | +Since each implementation derived from its inteface as well as its implementation base class we get a latice (DAG): |
| 6193 | + |
| 6194 | + Smiley -> Circle -> Shape |
| 6195 | + ^ ^ ^ |
| 6196 | + | | | |
| 6197 | + Impl::Smiley -> Impl::Circle -> Impl::Shape |
| 6198 | + |
| 6199 | +As mentioned, this is just one way to construct a dual hierarchy. |
| 6200 | + |
| 6201 | +Another (related) technique for separating interface and implementation is [PIMPL](#???). |
| 6202 | + |
| 6203 | +##### Note |
| 6204 | + |
| 6205 | +There is often a choice between offering common functionality as (implemented) base class funcetions and free-standing functions |
| 6206 | +(in an implementation namespace). |
| 6207 | +Base classes gives a shorter notation and easier access to shared data (in the base) |
| 6208 | +at the cost of the functionality being available only to users of the hierarchy. |
6013 | 6209 |
|
6014 | 6210 | ##### Enforcement
|
6015 | 6211 |
|
6016 |
| -??? |
| 6212 | +* Flag a derived to base conversion to a base with both data and virtual functions |
| 6213 | +(except for calls from a derived class memvber to a base class member) |
| 6214 | +* ??? |
| 6215 | + |
6017 | 6216 |
|
6018 | 6217 | ### <a name="Rh-copy"></a>C.130: Redefine or prohibit copying for a base class; prefer a virtual `clone` function instead
|
6019 | 6218 |
|
@@ -7015,7 +7214,8 @@ Naked unions are a source of type errors.
|
7015 | 7214 |
|
7016 | 7215 | # <a name="S-enum"></a>Enum: Enumerations
|
7017 | 7216 |
|
7018 |
| -Enumerations are used to define sets of integer values and for defining types for such sets of values. There are two kind of enumerations, "plain" `enum`s and `class enum`s. |
| 7217 | +Enumerations are used to define sets of integer values and for defining types for such sets of values. |
| 7218 | +There are two kind of enumerations, "plain" `enum`s and `class enum`s. |
7019 | 7219 |
|
7020 | 7220 | Enumeration rule summary:
|
7021 | 7221 |
|
|
0 commit comments