Archive-name: C++-faq/part10
Posting-Frequency: monthly Last-modified: Jun 17, 2002 URL: http://www.parashift.com/c++-faq-lite/ See reader questions & answers on this topic! - Help others by sharing your knowledge AUTHOR: Marshall Cline / cline@parashift.com / 972-931-9470 COPYRIGHT: This posting is part of "C++ FAQ Lite." The entire "C++ FAQ Lite" document is Copyright(C)1991-2002 Marshall Cline, Ph.D., cline@parashift.com. All rights reserved. Copying is permitted only under designated situations. For details, see section [1]. NO WARRANTY: THIS WORK IS PROVIDED ON AN "AS IS" BASIS. THE AUTHOR PROVIDES NO WARRANTY WHATSOEVER, EITHER EXPRESS OR IMPLIED, REGARDING THE WORK, INCLUDING WARRANTIES WITH RESPECT TO ITS MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. C++-FAQ-Lite != C++-FAQ-Book: This document, C++ FAQ Lite, is not the same as the C++ FAQ Book. The book (C++ FAQs, Cline and Lomow, Addison-Wesley) is 500% larger than this document, and is available in bookstores. For details, see section [3]. ============================================================================== SECTION [21]: Inheritance -- proper inheritance and substitutability [21.1] Should I hide member functions that were public in my base class? Never, never, never do this. Never. Never! Attempting to hide (eliminate, revoke, privatize) inherited public member functions is an all-too-common design error. It usually stems from muddy thinking. (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) ============================================================================== [21.2] Derived* --> Base* works OK; why doesn't Derived** --> Base** work? C++ allows a Derived* to be converted to a Base*, since a Derived object is a kind of a Base object. However trying to convert a Derived** to a Base** is flagged as an error. Although this error may not be obvious, it is nonetheless a good thing. For example, if you could convert a Car** to a Vehicle**, and if you could similarly convert a NuclearSubmarine** to a Vehicle**, you could assign those two pointers and end up making a Car* point at a NuclearSubmarine: class Vehicle { public: virtual ~Vehicle() { } virtual void startEngine() = 0; }; class Car : public Vehicle { public: virtual void startEngine(); virtual void openGasCap(); }; class NuclearSubmarine : public Vehicle { public: virtual void startEngine(); virtual void fireNuclearMissle(); }; int main() { Car car; Car* carPtr = &car; Car** carPtrPtr = &carPtr; Vehicle** vehiclePtrPtr = carPtrPtr; // This is an error in C++ NuclearSubmarine sub; NuclearSubmarine* subPtr = ⊂ *vehiclePtrPtr = subPtr; // This last line would have caused carPtr to point to sub ! carPtr->openGasCap(); // This might call fireNuclearMissle()! } In other words, if it was legal to convert a Derived** to a Base**, the Base** could be dereferenced (yielding a Base*), and the Base* could be made to point to an object of a different derived class, which could cause serious problems for national security (who knows what would happen if you invoked the openGasCap() member function on what you thought was a Car, but in reality it was a NuclearSubmarine!! Try the above code out and see what it does -- on most compilers it will call NuclearSubmarine::fireNuclearMissle()! (BTW you'll need to use a pointer cast to get it to compile. Suggestion: try to compile it without a pointer cast to see what the compiler does. If you're really quiet when the error message appears on the screen, you should be able to hear the muffled voice of your compiler pleading with you, "Please don't use a pointer cast! Pointer casts prevent me from telling you about errors in your code, but they don't make your errors go away! Pointer casts are evil[26.10]!" At least that's what my compiler says.) (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) ============================================================================== [21.3] Is a parking-lot-of-Car a kind-of parking-lot-of-Vehicle? Nope. I know it sounds strange, but it's true. You can think of this as a direct consequence of the previous FAQ, or you can reason it this way: if the kind-of relationship were valid, then someone could point a parking-lot-of-Vehicle pointer at a parking-lot-of-Car. But parking-lot-of-Vehicle has a addNewVehicleToParkingLot(Vehicle&) member function which can add any Vehicle object to the parking lot. This would allow you to park a NuclearSubmarine in a parking-lot-of-Car. Certainly it would be surprising if someone removed what they thought was a Car from the parking-lot-of-Car, only to find that it is actually a NuclearSubmarine. Another way to say this truth: a container of Thing is not a kind-of container of Anything even if a Thing is a kind-of an Anything. Swallow hard; it's true. You don't have to like it. But you do have to accept it. One last example which we use in our OO/C++ training courses: "A Bag-of-Apple is not a kind-of Bag-of-Fruit." If a Bag-of-Apple could be passed as a Bag-of-Fruit, someone could put a Banana into the Bag, even though it is supposed to only contain Apples! (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) ============================================================================== [21.4] Is an array of Derived a kind-of array of Base? Nope. This is a corollary of the previous FAQ. Unfortunately this one can get you into a lot of hot water. Consider this: class Base { public: virtual void f(); // 1 }; class Derived : public Base { public: // ... private: int i_; // 2 }; void userCode(Base* arrayOfBase) { arrayOfBase[1].f(); // 3 } int main() { Derived arrayOfDerived[10]; // 4 userCode(arrayOfDerived); // 5 } The compiler thinks this is perfectly type-safe. Line 5 converts a Derived* to a Base*. But in reality it is horrendously evil: since Derived is larger than Base, the pointer arithmetic done on line 3 is incorrect: the compiler uses sizeof(Base) when computing the address for arrayOfBase[1], yet the array is an array of Derived, which means the address computed on line 3 (and the subsequent invocation of member function f()) isn't even at the beginning of any object! It's smack in the middle of a Derived object. Assuming your compiler uses the usual approach to virtual[20] functions, this will reinterpret the int i_ of the first Derived as if it pointed to a virtual table, it will follow that "pointer" (which at this point means we're digging stuff out of a random memory location), and grab one of the first few words of memory at that location and interpret them as if they were the address of a C++ member function, then load that (random memory location) into the instruction pointer and begin grabbing machine instructions from that memory location. The chances of this crashing are very high. The root problem is that C++ can't distinguish between a pointer-to-a-thing and a pointer-to-an-array-of-things. Naturally C++ "inherited" this feature from C. NOTE: If we had used an array-like class (e.g., std::vector<Derived> from the standard library[34.1]) instead of using a raw array, this problem would have been properly trapped as an error at compile time rather than a run-time disaster. (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) ============================================================================== [21.5] Does array-of-Derived is-not-a-kind-of array-of-Base mean arrays are bad? Yes, arrays are evil[33.1]. (only half kidding). Seriously, arrays are very closely related to pointers, and pointers are notoriously difficult to deal with. But if you have a complete grasp of why the above few FAQs were a problem from a design perspective (e.g., if you really know why a container of Thing is not a kind-of container of Anything), and if you think everyone else who will be maintaining your code also has a full grasp on these OO design truths, then you should feel free to use arrays. But if you're like most people, you should use a template container class such as std::vector<T> from the standard library[34.1] rather than raw arrays. (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) ============================================================================== [21.6] Is a Circle a kind-of an Ellipse? Depends. But not if Ellipse guarantees it can change its size asymmetrically. For example, if Ellipse has a setSize(x,y) member function that promises the object's width() will be x and its height() will be y, Circle can't be a kind-of Ellipse. Simply put, if Ellipse can do something Circle can't, then Circle can't be a kind of Ellipse. This leaves two potential (valid) relationships between Circle and Ellipse: * Make Circle and Ellipse completely unrelated classes * Derive Circle and Ellipse from a base class representing "Ellipses that can't necessarily perform an unequal-setSize() operation" In the first case, Ellipse could be derived from class AsymmetricShape, and setSize(x,y) could be introduced in AsymmetricShape. However Circle could be derived from SymmetricShape which has a setSize(size) member function. In the second case, class Oval could only have setSize(size) which sets both the width() and the height() to size. Ellipse and Circle could both inherit from Oval. Ellipse --but not Circle-- could add the setSize(x,y) operation (but beware of the hiding rule[23.5] if the same member function name setSize() is used for both operations). (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) (Note: setSize(x,y) isn't sacred. Depending on your goals, it may be okay to prevent users from changing the dimensions of an Ellipse, in which case it would be a valid design choice to not have a setSize(x,y) method in Ellipse. However this series of FAQs discusses what to do when you want to create a derived class of a pre-existing base class that has an "unacceptable" method in it. Of course the ideal situation is to discover this problem when the base class doesn't yet exist. But life isn't always ideal...) ============================================================================== [21.7] Are there other options to the "Circle is/isnot kind-of Ellipse" dilemma? If you claim that all Ellipses can be squashed asymmetrically, and you claim that Circle is a kind-of Ellipse, and you claim that Circle can't be squashed asymmetrically, clearly you've got to adjust (revoke, actually) one of your claims. You can get rid of Ellipse::setSize(x,y), get rid of the inheritance relationship between Circle and Ellipse, or admit that your Circles aren't necessarily circular. (You can also get rid of Circle completely, where circleness is just a temporary state of an Ellipse object rather than a permanent quality of the object.) Here are the two most common traps new OO/C++ programmers regularly fall into. They attempt to use coding hacks to cover up a broken design (they redefine Circle::setSize(x,y) to throw an exception, call abort(), choose the average of the two parameters, or to be a no-op). Unfortunately all these hacks will surprise users, since users are expecting width() == x and height() == y. The one thing you must not do is surprise your users. If it is important to you to retain the "Circle is a kind-of Ellipse" inheritance relationship, you can weaken the promise made by Ellipse's setSize(x,y). E.g., you could change the promise to, "This member function might set width() to x and/or it might set height() to y, or it might do nothing". Unfortunately this dilutes the contract into dribble, since the user can't rely on any meaningful behavior. The whole hierarchy therefore begins to be worthless (it's hard to convince someone to use an object if you have to shrug your shoulders when asked what the object does for them). (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) (Note: setSize(x,y) isn't sacred. Depending on your goals, it may be okay to prevent users from changing the dimensions of an Ellipse, in which case it would be a valid design choice to not have a setSize(x,y) method in Ellipse. However this series of FAQs discusses what to do when you want to create a derived class of a pre-existing base class that has an "unacceptable" method in it. Of course the ideal situation is to discover this problem when the base class doesn't yet exist. But life isn't always ideal...) ============================================================================== [21.8] But I have a Ph.D. in Mathematics, and I'm sure a Circle is a kind of an Ellipse! Does this mean Marshall Cline is stupid? Or that C++ is stupid? Or that OO is stupid? Actually, it doesn't mean any of these things. But I'll tell you what it does mean (you may not like what I'm about to say): it means your intuitive notion of "kind of" leading you to make bad inheritance decisions. Your tummy is lying to you about what good inheritance really means -- stop believing those lies. Look, I have received and answered dozens of passionate e-mail messages about this subject. I have taught it hundreds of times to thousands of software professionals all over the place. I know it goes against your intuition. But trust me; your intuition is wrong (where "wrong" means "will cause you to make bad decisions in OO design and programming"). Here's how to make good decisions in OO design and programming (at least those decisions that have to do with inheritance): recognize that the derived class objects must be substitutable for the base class objects. That means objects of the derived class must behave in a manner consistent with the promises made in the base class' contract. Once you believe this (and I fully recognize that you might not yet, but you will if you work at it with an open mind), you'll see that setSize(x,y) violates this substitutability. There are three ways to fix this problem: 1. Soften the promises made by setSize(x,y) in base class Ellipse (or perhaps remove it completely), at the risk of breaking existing code that calls setSize(x,y). 2. Strengthen the promises made by setSize(x,y) in the derived class Circle (which really means allowing a Circle to have a different height than width -- an asymmetrical circle; hmmm). 3. Drop the inheritance relationship (possibly getting rid of class Circle completely; i.e., considering circleness to be a temporary state of an Ellipse rather than a permanent constraint on the object). Sorry, but there simply are no other choices. Another way to say this is that you have to either make the base class weaker (in this case weaken Ellipse to the point that it no longer guarantees you can set its width and height to different values), make the derived class stronger (in this case empower a Circle with the ability to be both symmetric and, ahem, asymmetric), or admit that a Circle is not substitutable for Ellipse. Important: there really are no other choices than the above three. In particular: 1. PLEASE don't write me and tell me that a fourth option is to derive both Circle and Ellipse from a third common base class. That's not a fourth solution. That's just a repackaging of solution #3: it works precisely because it removes the inheritance relationship between Circle and Ellipse. 2. PLEASE don't write me and tell me that a fourth option is to prevent users from changing the dimensions of an "Ellipse." That is not a fourth solution. That's just a repackaging of solution #1: it works precisely because it removes that guarantee that setSize(x,y) actually sets the width and height. 3. PLEASE don't write me and tell me that you've decided one of these three is "the best" solution. Doing that would show you had missed the whole point of this FAQ (that bad inheritance is subtle but fortunately you have three [not one; not two; but three] possible ways to dig yourself out). So when you run into bad inheritance, please try all three of these techniques and select the best (or perhaps "least bad") of the three. Don't throw out two of these tools ahead of time: try them all. (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) ============================================================================== [21.9] Perhaps Ellipse should inherit from Circle then? If Circle is the base class and Ellipse is the derived class, then you run into a whole new set of problems. For example, suppose Circle has a radius() method. Then Ellipse will also need to have a radius() method, but that doesn't make much sense: what does it even mean for a (possibly assymetric) ellipse to have a radius? If you get over that hurdle (e.g., by having Ellipse::radius() return the average of the major and minor axes, or whatever), then there is a problem with the relationship between radius() and area(). E.g., suppose Circle has an area() method that promises to return 3.14159[etc] times the square whatever radius() returns. Then either Ellipse::area() will not return the true area of the ellipse, or you'll have to stand on your head to get radius() to return something that matches the above formula. Even if you get past that one (i.e., by having Ellipse::radius() return the square root of the ellipse's area divided by pi), you'll get stuck by the circumference() method. E.g., suppose Circle has a circumference() method that promises to return two times pi times whatever is returned by radius(). Now you're stuck: there's no way to make all those constraints work out for Ellipse: the Ellipse class will have to lie about its area, its circumference, or both. Bottom line: you can make anything inherit from anything provided the methods in the derived class abide by the promises made in the base class. But you ought not to use inheritance just because you feel like it, or just because you want to get code reuse. You should use inheritance (a) only if the derived class's methods can abide by all the promises made in the base class, and (b) only if you don't think you'll confuse your users, and (c) only if there's something to be gained by using the inheritance -- some real, measurable improvement in time, money or risk. ============================================================================== [21.10] But my problem doesn't have anything to do with circles and ellipses, so what good is that silly example to me? Ahhh, there's the rub. You think the Circle/Ellipse example is just a silly example. But in reality, your problem is an isomorphism to that example. I don't care what your inheritance problem is, but all (yes all) bad inheritances boil down to the Circle-is-not-a-kind-of-Ellipse example. Here's why: Bad inheritances always have a base class with an extra capability (often an extra member function or two; sometimes an extra promise made by one or a combination of member functions) that a derived class can't satisfy. You've either got to make the base class weaker, make the derived class stronger, or eliminate the proposed inheritance relationship. I've seen lots and lots and lots of these bad inheritance proposals, and believe me, they all boil down to the Circle/Ellipse example. Therefore, if you truly understand the Circle/Ellipse example, you'll be able to recognize bad inheritance everywhere. If you don't understand what's going on with the Circle/Ellipse problem, the chances are high that you'll make some very serious and very expensive inheritance mistakes. Sad but true. (Note: this FAQ has to do with public inheritance; private and protected inheritance[24] are different.) ============================================================================== [21.11] If SortedList has exactly the same public interface as List, is SortedList a kind-of List? [NEW!] [Recently created thanks to a conversation with Dave Mikesell (in 6/02).] Probably not. The most important insight is that the answer depends on the details of the base class's contract. It is not enough to know that the public interfaces are compatible; one also needs to know if the contracts are compatible. The important word in the previous sentence is "contract." That word goes well beyond the public interface = method signatures = method names and parameter types and constness. A method's contract means its advertised behavior = advertised requirements and promises = advertised preconditions and postconditions. So if the base class has a method void insert(const Foo& x), the contract of that method includes the signature (meaning the name insert and the parameter const Foo&), but goes well beyond that to include the advertised behavior of that method. The other important word is advertised. The intention here is to differentiate between the code inside the method (assuming the base class's method has code; i.e., assuming it's not an unimplemented pure virtual function) and the promises made outside the method. This is where things get tricky. Suppose the insert(const Foo& x) method inserts a copy of x at the end of this List, and the override of that method in SortedList inserts x in the proper sort-order. Even though the override behaves in a way that is incompatible with the base class's code, the inheritance might still be proper if the base class makes a "weak" or "adaptable" promise. For example, if the advertised promise of the base class's insert(const Foo& x) method is something vague like, "Promises a copy of x will be inserted somewhere within this List," then the inheritance is probably okay since the override abides by the advertised behavior even though it is incompatible with the implemented behavior. The derived class needs to do what the base class promises to do, not what it actually does. The key is that we've separated the advertised behavior ("specification") from implemented behavior ("implementation"), and we rely on the specification rather than the implementation. This is very important because in a large percentage of the cases the base class's method is an unimplemented pure virtual -- the only thing that can be relied on is the specification -- there simply is no implementation on which to rely. Back to SortedList and List: it seems likely that List has one or more methods that have contracts which guarantee order, and therefore SortedList is probably not a kind-of List. For example, if List has a method that lets you reorder things, prepend things, append things, or change the ith element, and if those methods make the typical advertised promise, then SortedList would need to violate that advertised behavior and the inheritance would be improper. But it all depends on what the base class advertises -- on the base class's contract. ============================================================================== SECTION [22]: Inheritance -- abstract base classes (ABCs) [22.1] What's the big deal of separating interface from implementation? Interfaces are a company's most valuable resources. Designing an interface takes longer than whipping together a concrete class which fulfills that interface. Furthermore interfaces require the time of more expensive people. Since interfaces are so valuable, they should be protected from being tarnished by data structures and other implementation artifacts. Thus you should separate interface from implementation. ============================================================================== [22.2] How do I separate interface from implementation in C++ (like Modula-2)? Use an ABC[22.3]. ============================================================================== [22.3] What is an ABC? An abstract base class. At the design level, an abstract base class (ABC) corresponds to an abstract concept. If you asked a mechanic if he repaired vehicles, he'd probably wonder what kind-of vehicle you had in mind. Chances are he doesn't repair space shuttles, ocean liners, bicycles, or nuclear submarines. The problem is that the term "vehicle" is an abstract concept (e.g., you can't build a "vehicle" unless you know what kind of vehicle to build). In C++, class Vehicle would be an ABC, with Bicycle, SpaceShuttle, etc, being derived classes (an OceanLiner is-a-kind-of-a Vehicle). In real-world OO, ABCs show up all over the place. At the programming language level, an ABC is a class that has one or more pure virtual[22.4] member functions. You cannot make an object (instance) of an ABC. ============================================================================== [22.4] What is a "pure virtual" member function? A member function declaration that turns a normal class into an abstract class (i.e., an ABC). You normally only implement it in a derived class. Some member functions exist in concept; they don't have any reasonable definition. E.g., suppose I asked you to draw a Shape at location (x,y) that has size 7. You'd ask me "what kind of shape should I draw?" (circles, squares, hexagons, etc, are drawn differently). In C++, we must indicate the existence of the draw() member function (so users can call it when they have a Shape* or a Shape&), but we recognize it can (logically) be defined only in derived classes: class Shape { public: virtual void draw() const = 0; // = 0 means it is "pure virtual" // ... }; This pure virtual function makes Shape an ABC. If you want, you can think of the "= 0;" syntax as if the code were at the NULL pointer. Thus Shape promises a service to its users, yet Shape isn't able to provide any code to fulfill that promise. This forces any actual object created from a [concrete] class derived from Shape to have the indicated member function, even though the base class doesn't have enough information to actually define it yet. Note that it is possible to provide a definition for a pure virtual function, but this usually confuses novices and is best avoided until later. ============================================================================== [22.5] How do you define a copy constructor or assignment operator for a class that contains a pointer to a (abstract) base class? If the class "owns" the object pointed to by the (abstract) base class pointer, use the Virtual Constructor Idiom[20.6] in the (abstract) base class. As usual with this idiom, we declare a pure virtual[22.4] clone() method in the base class: class Shape { public: // ... virtual Shape* clone() const = 0; // The Virtual (Copy) Constructor[20.6] // ... }; Then we implement this clone() method in each derived class: class Circle : public Shape { public: // ... virtual Shape* clone() const { return new Circle(*this); } // ... }; class Square : public Shape { public: // ... virtual Shape* clone() const { return new Square(*this); } // ... }; Now suppose that each Fred object "has-a" Shape object. Naturally the Fred object doesn't know whether the Shape is Circle or a Square or ... Fred's copy constructor and assignment operator will invoke Shape's clone() method to copy the object: class Fred { public: Fred(Shape* p) : p_(p) { assert(p != NULL); } // p must not be NULL ~Fred() { delete p_; } Fred(const Fred& f) : p_(f.p_->clone()) { } Fred& operator= (const Fred& f) { if (this != &f) { // Check for self-assignment Shape* p2 = f.p_->clone(); // Create the new one FIRST... delete p_; // ...THEN delete the old one p_ = p2; } return *this; } // ... private: Shape* p_; }; ============================================================================== SECTION [23]: Inheritance -- what your mother never told you [23.1] Is it okay for a non-virtual function of the base class to call a virtual function? Yes. It's sometimes (not always!) a great idea. For example, suppose all Shape objects have a common algorithm for printing, but this algorithm depends on their area and they all have a potentially different way to compute their area. In this case Shape's area() method would necessarily have to be virtual (probably pure virtual) but Shape::print() could, if we were guaranteed no derived class wanted a different algorithm for printing[23.4], be a non-virtual defined in the base class Shape. #include "Shape.hpp" void Shape::print() const { float a = this->area(); // area() is pure virtual // ... } ============================================================================== [23.2] That last FAQ confuses me. Is it a different strategy from the other ways to use virtual functions? What's going on? Yes, it is a different strategy. Yes, there really are two different basic ways to use virtual functions: 1. Suppose you have the situation described in the previous FAQ: you have a method whose overall structure is the same for each derived class, but has little pieces that are different in each derived class. So the algorithm is the same, but the primitives are different. In this case you'd write the overall algorithm in the base class as a public method (that's sometimes non-virtual), and you'd write the little pieces in the derived classes. The little pieces would be declared in the base class (they're often protected, they're often pure virtual, and they're certainly virtual), and they'd ultimately be defined in each derived class. The most critical question in this situation is whether or not the public method containing the overall algorithm should be virtual. The answer is to make it virtual if you think that some derived class might need to override it[23.4]. 2. Suppose you have the exact opposite situation from the previous FAQ, where you have a method whose overall structure is different in each derived class, yet it has little pieces that are the same in most (if not all) derived classes. In this case you'd put the overall algorithm in a public virtual that's ultimately defined in the derived classes, and the little pieces of common code can be written once (to avoid code duplication) and stashed somewhere (anywhere!). A common place to stash the little pieces is in the protected part of the base class, but that's not necessary and it might not even be best. Just find a place to stash them and you'll be fine. Note that if you do stash them in the base class, you should normally make them protected, since normally they do things that public users don't need/want to do. Assuming they're protected, they probably shouldn't be virtual: if the derived class doesn't like the behavior in one of them, it doesn't have to call that method. For emphasis, the above list is a both/and situation, not an either/or situation. In other words, you don't have to choose between these two strategies on any given class. It's perfectly normal to have method f() correspond to strategy #1 while method g() corresponds to strategy #2. In other words, it's perfectly normal to have both strategies working in the same class. ============================================================================== [23.3] When my base class's constructor calls a virtual function, why doesn't my derived class's override of that virtual function get invoked? During the class Base's constructor, the object isn't yet a Derived, so if Base::Base() calls a virtual[20] function virt(), the Base::virt() will be invoked, even if Derived::virt() exists. Similarly, during Base's destructor, the object is no longer a Derived, so when Base::~Base() calls virt(), Base::virt() gets control, not the Derived::virt() override. You'll quickly see the wisdom of this approach when you imagine the disaster if Derived::virt() touched a member object from class Derived. In particular, if Base::Base() called the virtual function virt(), this rule causes Base::virt() to be invoked. If it weren't for this rule, Derived::virt() would get called before the Derived part of a Derived object is constructed, and Derived::virt() could touch unconstructed member objects from the Derived part of a Derived object. That would be a disaster. ============================================================================== [23.4] Should a derived class replace ("override") a non-virtual function from a base class? It's legal, but it ain't moral. Experienced C++ programmers will sometimes redefine a non-virtual function for efficiency (e.g., if the derived class implementation can make better use of the derived class's resources) or to get around the hiding rule[23.5]. However the client-visible effects must be identical, since non-virtual functions are dispatched based on the static type of the pointer/reference rather than the dynamic type of the pointed-to/referenced object. ============================================================================== [23.5] What's the meaning of, Warning: Derived::f(float) hides Base::f(int)? It means you're going to die. Here's the mess you're in: if Base declares a member function f(int), and Derived declares a member function f(float) (same name but different parameter types and/or constness), then the Base f(int) is "hidden" rather than "overloaded" or "overridden" (even if the Base f(int) is virtual[20]). Here's how you get out of the mess: Derived must have a using declaration of the hidden member function. For example, class Base { public: void f(int); }; class Derived : public Base { public: using Base::f; // This un-hides Base::f(int) void f(double); }; If the using syntax isn't supported by your compiler, redefine the hidden Base member function(s), even if they are non-virtual[23.4]. Normally this re-definition merely calls the hidden Base member function using the :: syntax. E.g., class Derived : public Base { public: void f(double); void f(int i) { Base::f(i); } // The redefinition merely calls Base::f(int) }; ============================================================================== [23.6] What does it mean that the "virtual table" is an unresolved external? If you get a link error of the form "Error: Unresolved or undefined symbols detected: virtual table for class Fred," you probably have an undefined virtual[20] member function in class Fred. The compiler typically creates a magical data structure called the "virtual table" for classes that have virtual functions (this is how it handles dynamic binding[20.2]). Normally you don't have to know about it at all. But if you forget to define a virtual function for class Fred, you will sometimes get this linker error. Here's the nitty gritty: Many compilers put this magical "virtual table" in the compilation unit that defines the first non-inline virtual function in the class. Thus if the first non-inline virtual function in Fred is wilma(), the compiler will put Fred's virtual table in the same compilation unit where it sees Fred::wilma(). Unfortunately if you accidentally forget to define Fred::wilma(), rather than getting a Fred::wilma() is undefined, you may get a "Fred's virtual table is undefined". Sad but true. ============================================================================== [23.7] How can I set up my class so it won't be inherited from? This is known as making the class "final" or "a leaf." There are two ways to do it: an easy technical approach and an even easier non-technical approach. * The (easy) technical approach is to make the class's constructors private and to use the Named Constructor Idiom[10.8] to create the objects. No one can create objects of a derived class since the base class's constructor will be inaccessible. The "named constructors" themselves could return by pointer if you want your objects allocated by new[16.20] or they could return by value if you want the objects created on the stack[10.8]. * The (even easier) non-technical approach is to put a big fat ugly comment next to the class definition. The comment could say, for example, // We'll fire you if you inherit from this class or even just /*final*/ class Whatever {...};. Some programmers balk at this because it is enforced by people rather than by technology, but don't knock it on face value: it is quite effective in practice. ============================================================================== [23.8] How can I set up my member function so it won't be overridden in a derived class? This is known as making the method "final" or "a leaf." Here's an easy-to-use solution to this that gives you 90+% of what you want: simply add a comment next to the method and rely on code reviews or random maintenance activities to find violators. The comment could say, for example, // We'll fire you if you override this method or perhaps more likely, /*final*/ void theMethod();. The advantages to this technique are (a) it is extremely easy/fast/inexpensive to use, and (b) it is quite effective in practice. In other words, you get 90+% of the benefit with almost no cost -- lots of bang per buck. (I'm not aware of a "100% solution" to this problem so this may be the best you can get. If you know of something better, please feel free to email me. But please do not email me objecting to this solution because it's low-tech or because it doesn't "prevent" people from doing the wrong thing. Who cares whether it's low-tech or high-tech as long as it's effective?!? And nothing in C++ "prevents" people from doing the wrong thing. Using pointer casts[26.10] and pointer arithmetic, people can do just about anything they want. C++ makes it easy to do the right thing, but it doesn't prevent espionage[16.24]. Besides, the original question (see above) asked for something so people won't do the wrong thing, not so they can't do the wrong thing.) In any case, this solution should give you most of the potential benefit at almost no cost. ============================================================================== SECTION [24]: Inheritance -- private and protected inheritance [24.1] How do you express "private inheritance"? When you use : private instead of : public. E.g., class Foo : private Bar { public: // ... }; ============================================================================== [24.2] How are "private inheritance" and "composition" similar? private inheritance is a syntactic variant of composition (AKA aggregation and/or has-a). E.g., the "Car has-a Engine" relationship can be expressed using simple composition: class Engine { public: Engine(int numCylinders); void start(); // Starts this Engine }; class Car { public: Car() : e_(8) { } // Initializes this Car with 8 cylinders void start() { e_.start(); } // Start this Car by starting its Engine private: Engine e_; // Car has-a Engine }; The "Car has-a Engine" relationship can also be expressed using private inheritance: class Car : private Engine { // Car has-a Engine public: Car() : Engine(8) { } // Initializes this Car with 8 cylinders using Engine::start; // Start this Car by starting its Engine }; There are several similarities between these two variants: * In both cases there is exactly one Engine member object contained in every Car object * In neither case can users (outsiders) convert a Car* to an Engine* * In both cases the Car class has a start() method that calls the start() method on the contained Engine object. There are also several distinctions: * The simple-composition variant is needed if you want to contain several Engines per Car * The private-inheritance variant can introduce unnecessary multiple inheritance * The private-inheritance variant allows members of Car to convert a Car* to an Engine* * The private-inheritance variant allows access to the protected members of the base class * The private-inheritance variant allows Car to override Engine's virtual[20] functions * The private-inheritance variant makes it slightly simpler (20 characters compared to 28 characters) to give Car a start() method that simply calls through to the Engine's start() method Note that private inheritance is usually used to gain access into the protected members of the base class, but this is usually a short-term solution (translation: a band-aid[24.3]). ============================================================================== [24.3] Which should I prefer: composition or private inheritance? Use composition when you can, private inheritance when you have to. Normally you don't want to have access to the internals of too many other classes, and private inheritance gives you some of this extra power (and responsibility). But private inheritance isn't evil; it's just more expensive to maintain, since it increases the probability that someone will change something that will break your code. A legitimate, long-term use for private inheritance is when you want to build a class Fred that uses code in a class Wilma, and the code from class Wilma needs to invoke member functions from your new class, Fred. In this case, Fred calls non-virtuals in Wilma, and Wilma calls (usually pure virtuals[22.4]) in itself, which are overridden by Fred. This would be much harder to do with composition. class Wilma { protected: void fredCallsWilma() { std::cout << "Wilma::fredCallsWilma()\n"; wilmaCallsFred(); } virtual void wilmaCallsFred() = 0; // A pure virtual function[22.4] }; class Fred : private Wilma { public: void barney() { std::cout << "Fred::barney()\n"; Wilma::fredCallsWilma(); } protected: virtual void wilmaCallsFred() { std::cout << "Fred::wilmaCallsFred()\n"; } }; ============================================================================== [24.4] Should I pointer-cast from a private derived class to its base class? Generally, No. From a member function or friend[14] of a privately derived class, the relationship to the base class is known, and the upward conversion from PrivatelyDer* to Base* (or PrivatelyDer& to Base&) is safe; no cast is needed or recommended. However users of PrivatelyDer should avoid this unsafe conversion, since it is based on a private decision of PrivatelyDer, and is subject to change without notice. ============================================================================== [24.5] How is protected inheritance related to private inheritance? Similarities: both allow overriding virtual[20] functions in the private/protected base class, neither claims the derived is a kind-of its base. Dissimilarities: protected inheritance allows derived classes of derived classes to know about the inheritance relationship. Thus your grand kids are effectively exposed to your implementation details. This has both benefits (it allows derived classes of the protected derived class to exploit the relationship to the protected base class) and costs (the protected derived class can't change the relationship without potentially breaking further derived classes). Protected inheritance uses the : protected syntax: class Car : protected Engine { public: // ... }; ============================================================================== [24.6] What are the access rules with private and protected inheritance? Take these classes as examples: class B { /*...*/ }; class D_priv : private B { /*...*/ }; class D_prot : protected B { /*...*/ }; class D_publ : public B { /*...*/ }; class UserClass { B b; /*...*/ }; None of the derived classes can access anything that is private in B. In D_priv, the public and protected parts of B are private. In D_prot, the public and protected parts of B are protected. In D_publ, the public parts of B are public and the protected parts of B are protected (D_publ is-a-kind-of-a B). class UserClass can access only the public parts of B, which "seals off" UserClass from B. To make a public member of B so it is public in D_priv or D_prot, state the name of the member with a B:: prefix. E.g., to make member B::f(int,float) public in D_prot, you would say: class D_prot : protected B { public: using B::f; // Note: Not using B::f(int,float) }; ============================================================================== User Contributions:Comment about this article, ask questions, or add new information about this topic: |
write my essay https://custompaperwritersservices.com cheap essay writer service https://essaywritingservicelinked.com