资源描述
Viewing Hints Book Home Page Free Newsletter Seminars Seminars on CD ROM Consulting Annotated Solution GuideRevision 1.0for Thinking in C+, 2nd edition, Volume 1by Chuck Allison2001 MindView, Inc. All Rights Reserved. Previous Chapter Table of Contents Next Chapter Chapter 1414-1Modify Car.cpp so that it also inherits from a class called Vehicle, placing appropriate member functions in Vehicle (that is, make up some member functions). Add a nondefault constructor to Vehicle, which you must call inside Cars constructor.14-2Create two classes, Aand B,with default constructors that announce themselves. Inherit a new class called C from A, and create a member object of Bin C, but do not create a constructor for C. Create an object of class Cand observe the results.14-3Create two classes, Aand B,with default constructors that announce themselves. Inherit a new class called C from A, and create a member object of Bin C, but do not create a constructor for C. Create an object of class Cand observe the results.Solution:请预览后下载!/: S14:DefaultConstruction.cpp#include using namespace std;class A public: A() cout A:A()n;class B public: B() cout B:B()n;class C : public A B b;int main() C c;请预览后下载!/* Output:A:A()B:B()*/:The “parts” of a C object are automatically constructed, inherited sub-objects first, followed by contained objects.14-4Create a three-level hierarchy of classes with default constructors, along with destructors, both of which announce themselves to cout. Verify that for an object of the most derived type, all three constructors and destructors are automatically called. Explain the order in which the calls are made.Solution:/: S14:HierarchyConstruction.cpp#include using namespace std;class A public: A() cout A:A()n; A() cout A:A()n;请预览后下载!class B : public A public: B() cout B:B()n; B() cout B:B()n;class C : public B public: C() cout C:C()n; C() cout C:C()n;int main() C c;/* Output:A:A()B:B()C:C()C:C()请预览后下载!B:B()A:A()*/:An objects “parts” are always constructed before the object itself. Applying this principle recursively requires that the sub-object at the top of the hierarchy must be created first. Destruction is always the reverse order of construction.(Exercises 14-5 through 14-8 are left to the reader.)14-5In Combined.cpp, create a class D that inherits from B and has a member object of class C. Add code to show when the constructors and destructors are being called.14-6Modify Order.cpp to add another level of inheritance Derived3 with member objects of class Member4 and Member5. Trace the output of the program.14-7In NameHiding.cpp, verify that in Derived2, Derived3, and Derived4, none of the base-class versions of f( ) are available.14-8Modify NameHiding.cpp by adding three overloaded functions named h( ) to Base, and show that redefining one of them in a derived class hides the others.14-9Inherit a class StringVector from vector and redefine the push_back( ) and operator member functions to accept and produce string*. What happens if you try to push_back( ) a void*?请预览后下载!Solution:/: S14:StringVector.cpp/-msc VC+ only fakes reinterpret_cast#include #include #include #include / For size_tusing namespace std;class StringVector : public vector public: void push_back(string* s) vector:push_back(s); string*& operator(size_t n) return reinterpret_cast (vector:operator(n); const string* operator(size_t n) const return reinterpret_cast 请预览后下载! (vector:operator(n); ;int main() StringVector v; string s1(live), s2(long), s3(and), s4(prosper); v.push_back(&s1); v.push_back(&s2); v.push_back(&s3); v.push_back(&s4); for (size_t i = 0; i v.size(); +i) cout *vi endl; / void* p = &s1; / v.push_back(p); / error!/* Output:livelong请预览后下载!andprosper*/:The problem with the commented-out push_back( ) above is that there is no implicit conversion from a void* to any other type, as there is in C. This makes StringVector type-safe. Note that I overloaded both versions of operator (const and non-const).If youve done any amount of reading on object-oriented programming in C+, you probably have learned that you should never override a non-virtual function from a publicly-inherited class (see for example, Scott Meyers “Effective C+”, Item 37). The standard containers such as vector have no virtual functions at all, so there must be a better way to define StringVector. The Better Way is to use private inheritance, and provide access to the inherited functions as the following version illustrates.class StringVector : private vector public: void push_back(string* s) vector:push_back(s); string*& operator(size_t n) return reinterpret_cast (vector:operator(n); const string* operator(size_t n) const return reinterpret_cast 请预览后下载! (vector:operator(n); using vector:size;Private inheritance combined with templates makes building such type-safe containers fairly simple. (See Chapter 16 and Volume 2).(Exercises 14-10 through 14-13 are left to the reader.)14-10Write a class containing a long and use the psuedo-constructor call syntax in the constructor to initialize the long.14-11Create a class called Asteroid. Use inheritance to specialize the PStash class in Chapter 13 (PStash.h & PStash.cpp) so that it accepts and returns Asteroid pointers. Also modify PStashTest.cpp to test your classes. Change the class so PStash is a member object.14-12Repeat Exercise 11 with a vector instead of a PStash.14-13In SynthesizedFunctions.cpp, modify Chess to give it a default constructor, copy-constructor, and assignment operator. Demonstrate that youve written these correctly.14-14请预览后下载!Create two classes called Traveler and Pager without default constructors, but with constructors that take an argument of type string, which they simply copy to an internal string variable. For each class, write the correct copy-constructor and assignment operator. Now inherit a class BusinessTraveler from Travelerand give it a member object of type Pager. Write the correct default constructor, a constructor that takes a string argument, a copy-constructor, and an assignment operator.Solution:/: S14:BusinessTraveler.cpp#include #include using namespace std;class Traveler string str;public: Traveler(const string& s) : str(s) Traveler(const Traveler& t) : str(t.str) Traveler& operator=(const Traveler& t) if (this != &t) str = t.str; return *this; string getString() const return str;请预览后下载! ;class Pager string str;public: Pager(const string& s) : str(s) Pager(const Pager& p) : str(p.str) Pager& operator=(const Pager& p) if (this != &p) str = p.str; return *this; string getString() const return str; ;class BusinessTraveler : public Traveler Pager pager;public: BusinessTraveler() : Traveler(), pager() 请预览后下载! BusinessTraveler(const string& t, const string& p) : Traveler(t), pager(p) BusinessTraveler(const BusinessTraveler& b) : Traveler(b), pager(b.pager) BusinessTraveler& operator=(const BusinessTraveler& b) if (this != &b) Traveler:operator=(b); / Assign base part pager = b.pager; / Assign derived part return *this; friend ostream& operator(ostream& os, const BusinessTraveler& b) return os b.getString() , b.pager.getString() ; ;int main() BusinessTraveler b1(Joe BusinessMan, Pager 1); cout b1 endl; BusinessTraveler b2(Jane BusinessWoman, Pager 2);请预览后下载! cout b2 endl; BusinessTraveler b3; cout b3 endl; BusinessTraveler b4(b1); cout b4 endl; b3 = b2; cout b3 endl;/* Output:Joe BusinessMan, Pager 1Jane BusinessWoman, Pager 2, Joe BusinessMan, Pager 1Jane BusinessWoman, Pager 2*/:The solution is a little clearer if BusinessTraveler has a constructor that takes two arguments: one for the Travelers name and one for the Pager name. The key point of this exercise is to get the constructor and assignment operator correct with inheritance. Both need to make sure that the base class sub-object is taken care of (which is sometimes easy to forget!). In constructors you use the initializer list; in assignment operators you explicitly call the base class assignment operator (since the base class data is private, you have no choice).14-15Create a class with two static member functions. Inherit from this class and redefine one of the member functions. Show that the other is hidden in the derived class.请预览后下载!Solution:/: S14:StaticBaseMethods.cpp#include using namespace std;class Base public: static void f() cout Base:f()n; static void g() cout Base:g()n; ;class Derived : public Base public: static void g(int) cout Derived:g(int)n; ;请预览后下载!int main() Derived:f(); Derived:g(1); / Derived:g(); / Error: too few parameters/* Output:Base:f()Derived:g(int)*/:The fact that I introduced a parameter for g( ) in Derived is not significant; any function named the same as one in any base class hides all overloaded functions of the same name in the base classes (because derived classes constitute a nested scope).14-16Look up more of the member functions for ifstream. In FName2.cpp, try them out on the file object.(Left to the reader)14-17Use private and protected inheritance to create two new classes from a base class. Then attempt to upcast objects of the derived class to the base class. Explain what happens.请预览后下载!Solution:/: S14:InaccessibleBase.cpp/=M echo Compile InaccessibleBase.cpp by hand!class Base ;class Private : private Base ;class Protected : protected Base ;int main() Private pri; Protected pro; / Both statements are errors: Base* b = static_cast(&pri); b = static_cast(&pro);/* Output:Error Line 12: Cannot cast from Private * to Base * in function main()Error Line 13: Cannot cast from Protected * to Base * in function main()*/请预览后下载!/:Just like you cant access private or protected members in non-member or non-derived contexts (respectively), neither can you attempt an upcast to a private or protected base class. These types of inheritance are used for implementation only, therefore, clients that only have the public interface available have no such access.14-18In Protected.cpp, add a member function in Derived that calls the protected Base member read( ).(Left to the reader.)14-19Change Protected.cpp so that Derived is using protected inheritance. See if you can call value( ) for a Derived object.(Left to the reader.)14-20Create a class called SpaceShip with a fly( ) method. Inherit Shuttle from SpaceShip and add a land( ) method. Create a new Shuttle, upcast by pointer or reference to a SpaceShip, and try to call the land( ) method. Explain the results.Solution:/: S14:NarrowingCast.cpp/=M echo Compile NarrowingCast.cpp by hand!class SpaceShip public:请预览后下载! void fly() ;class Shuttle : public SpaceShip public: void land() ;int main() Shuttle shut; SpaceShip& ship = static_cast(shut); ship.land(); / Error!/* Output:Error Line 16: land is not a member of SpaceShip in function main()*/:No mystery here. The variable ship is a SpaceShip, which does not have a land( ) method. If SpaceShip had a land method, there would still be a problem: SpaceShip:land( ) would be called instead of Shuttle:land( ) (rarely the right thing to do!). This mystery is solved in the next chapter (read about virtual functions).请预览后下载!(Exercises 14-21 through 14-25 are left to the reader.)14-21Modify Instrument.cpp to add a prepare( ) method to Instrument. Call prepare( ) inside tune( ).14-22Modify Instrument.cpp so that play( ) prints a message to cout, and Wind redefines play( ) to print a different message to cout. Run the program and explain why you probably wouldnt want this behavior. Now put the virtual keyword (which you will learn about in Chapter 15) in front of the play( ) declaration in Instrument and observe the change in the behavior.14-23In CopyConstructor.cpp, inherit a new class from Child and give it a Member m. Write a proper constructor, copy-constructor, operator=, and operator for ostreams, and test the class in main( ).14-24Take the example CopyConstructor.cpp and modify it by adding your own copy-constructor to Child without calling the base-class copy-constructor and see what happens. Fix the problem by making a proper explicit call to the base-class copy constructor in the constructor-initializer list of the Child copy-constructor.14-25Modify InheritStack2.cpp to use a vectorinstead of a Stack.14-26Create a class Rock with a default constructor, a copy-constructor, an assignment operator, and a destructor, all of which announce to coutthat theyve been called. In main( ), create a vector (that is, hold Rock objects by value) and add some Rocks. Run the program and explain the output you get. Note whether the destructors are called for the 请预览后下载!Rock objects in the vector. Now repeat the exercise with a vector. Is it possible to create a vector?Solution:/: S14:LikeARock.cpp#include #include using namespace std;class Rock public: Rock() cout Rock()n; Rock(const Rock&) cout Rock(const Rock&)n; Rock& operator=(const Rock&) cout Rock()n; return *this; Rock() cout Rock()n;请预览后下载! int main() vector byValue; Rock r1, r2, r3; byValue.push_back(r1); byValue.push_back(r2); byValue.push_back(r3); cout byValue populatednn; vector byPtr; byPtr.push_back(&r1); byPtr.push_back(&r2); byPtr.push_back(&r3); cout byPtr populatednn;/* Output:Rock()Rock()Rock()Rock(const Rock&)Rock(const Rock&)请预览后下载!Rock(const Rock&)byValue populatedbyPtr populatedRock()Rock()Rock()Rock()Rock()Rock()*/:All the standard C+ containers store a copy of their elements (hence the calls to the copy constructor above). Even byPtr stores copies; theyre just copies of the pointers you sent to push_back( ). As you can also see, the destructor of byValue ensures that all its elements (copies) are destroyed. (Again, so does byPtr; pointers just dont have destructors, so you see no trace output). It makes no sense to have a vector of references, since vectors store copies. If you store heap pointers in a container, you are responsible to delete them.14-27This exercise creates the design pattern called proxy. Start with a base class Subjectand give it three functions: f( ), g( ), and h( ). Now inherit a class Proxy and two classes Implementation1 and Implementation2 from Subject. Proxy should contain a pointer to a Subject, and all the member functions for Proxy should just turn around and make the same calls through the Subject pointer. The Proxy constructor takes a pointer to a Subject that is installed in the Proxy(usually by the constructor). In main( ), create two different Proxy objects that use the two different implementations. Now modify 请预览后下载!Proxy so that you can dynamically change implementations.Solution:This is the well-known Proxy pattern from the Gang of Four (see Design Patterns, Gamma et al, Prentice-Hall, 1994). Heres a solution that allows changing the implementation at run-time:/: S14:Proxy.cpp#include using namespace std;class Subject public: virtual void f() = 0; virtual void g() = 0; virtual void h() = 0;class Proxy : public Subject Subject* pSubject;public: Proxy(Subject* pSubject = 0) this-pSubject = pSubject; 请预览后下载! void setSubject(Subject* pSubject) this-pSubject = pSubject; void f() pSubject-f(); void g() pSubject-g(); void h() pSubject-h(); ;class Implementation1 : public Subject public: void f() cout Implementation1:fn; void g() cout Implementation1:gn;请预览后下载! void h() cout Implementation1:hn; ;class Implementation2 : public Subject public: void f() cout Implementation2:fn; void g() cout Implementation2:gn; void h() cout Implementation2:hn; ;int main() Implementation1 impl1; Proxy proxy(&impl1);请预览后下载! proxy.f(); proxy.g(); proxy.h(); Implementation2 impl2; proxy.setSubject(&impl2); proxy.f(); proxy.g(); proxy.h(); /:Here is the output:Implementation1:fImplementation1:gImplementation1:hImplementation2:fImplementation2:gImplementation2:hThe Subject class is typically an abstract class, since it represents the interface of the functionality you want. The Implementation classes provide different implementations of the interface, of course. The proxy class also must formally implement the interface, since it is the primary object type the client uses. The flexibility of this pattern comes from the ability to change implementations without changing which object the client uses (yet another instance of the principle: “All problems can be solved by introducing another level of indirection”). This is a common technique to allow an object to “change colors” during its lifetime.请预览后下载!14-28Modify ArrayOperatorNew.cpp from Chapter 13 to show that, if you inherit from Widget, the allocation still works correctly. Explain why inheritance in Framis.cpp from Chapter 13 would not work correctly.(Left to the reader)14-29Modify Framis.cpp from Chapter 13 by inheriting from Framis and creating new versions of new and delete for your derived class. Demonstrate that they work correctly.(Left to the reader) Previous Chapter Table of Contents Next Chapter Last Update:06/27/2002 (注:可编辑下载,若有不当之处,请指正,谢谢!) 请预览后下载!
展开阅读全文