======Chapter.15====== Answers for chapter 15 ---- * //**Quote**// class implementation** [[cs:programming:cpp:cpp_primer:answers:chpt_15:routine#Quote Class|routine]]** ====Ex.15.1-15.10==== ==ex.15.1== >Exercise 15.1: What is a virtual member? Virtual members are those members who expect their derived classes to override. ==ex.15.2== >Exercise 15.2: How does the protected access specifier differ from private? * **Private members** are accessible from their class members, but not from their derived class members. * **Protected members** are accessible from both the base class members and the derived class members. ==ex.15.3== >Exercise 15.3: Define your own versions of the Quote class and the print_total function. CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_3.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_3.cc|source]] ==ex.15.4== >Exercise 15.4: Which of the following declarations, if any, are incorrect? Explain why. class Base { ... }; //error, a class cannot be derived from itself (a) class Derived : public Derived { ... }; //ok (b) class Derived : private Base { ... }; //error, the declaration of a derived class must not contain the derived list (c) class Derived : public Base; ==ex.15.5== >Exercise 15.5: Define your own version of the Bulk_quote class. CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_5.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_5.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_5_test.cc|test file]] ==ex.15.6== >Exercise 15.6: Test your print_total function from the exercises in § 15.2.1 (p. 595) by passing both Quote and Bulk_quote objects o that function. Please check the code in exercise 15.5. ==ex.15.7== >Exercise 15.7: Define a class that implements a limited discount strategy, which applies a discount to books purchased up to a given limit. If the number of copies exceeds that limit, the normal price applies to those purchased beyond the limit. CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_7.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_7.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_7_test.cc|test file]] ==ex.15.8== >Exercise 15.8: Define static type and dynamic type. * The static type of an expression is always known at **compile time**, it is the type with which a variable is **declared** or that an **expression yields**. * The dynamic type is the type of the object in memory that the variable or expression **represents**. The dynamic type may not be known //until run time//. ==ex.15.9== >Exercise 15.9: When is it possible for an expression’s static type to differ from its dynamic type? Give three examples in which the static and dynamic type differ. A dynamic type of an expression differs from a static type based on the type of the argument to which its corresponding parameter is bound. Assuming we have a base class ''Base'' and a derived class ''Derived'', the following binding might cause the dynamic type of the expression to differ from its static type: * Binding ''Derived'' type object to an ''Base&'' parameter * Binding ''Derived'' type object to an ''Base*'' parameter * Binding ''Derived*'' to ''Base*'', or ''Derived&'' to ''Base&'' ==ex.15.10== >Exercise 15.10: Recalling the discussion from §8.1 (p. 311), explain how the program on page 317 that passed an ifstream to the Sales_data read function works. - ''read'' function takes ''istream&'' type parameter. - we supplied an ''ifstream&'' type object as the argument. - Because ''ifstream'' is a derived class of ''istream'', the above binding will bind the base portion of the ''ifstream&'' object to the parameter, satisfying the requirement of ''read''. ==ex.15.11== >Exercise 15.11: Add a virtual debug function to your Quote class hierarchy that displays the data members of the respective classes. CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_11.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_11.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_11_test.cc|test file]] ==ex.15.12== >Exercise 15.12: Is it ever useful to declare a member function as both override and final? Why or why not? Yes. A final function means that subsequent classes cannot override it, although the function can still override the function it inherited. ==ex.15.13== >Exercise 15.13: Given the following classes, explain each print function. If there is a problem in this code, how would you fix it? class base { public: string name() { return basename; } virtual void print(ostream &os) { os << basename; } private: string basename; }; class derived : public base { public: void print(ostream &os) { print(os); os << " " << i; } //error, infinite recursive private: int i; }; * The ''base::print()'' will print its ''basename'' member * The ''derived::print()'' will print its ''basename'' and ''j'' member. However, the call in the example will result in an infinite recursive error since ''print(os)'' was supposed to call the base class version of ''print()'', but it failed to circumvent the virtual mechanism. The problem can be fixed by specifying the scope of the call: void print(ostream &os) { base::print(os); os << " " << i; } ==ex.15.14== >Exercise 15.14: Given the classes from the previous exercise and the following objects, determine which function is called at run time: base bobj; base *bp1 = &bobj; base &br1 = bobj; derived dobj; base *bp2 = &dobj; base &br2 = dobj; a) bobj.print(); //base::print(), compile time b) dobj.print(); //derived::print(), compile time c) bp1->name(); //base::print(), compile time d) bp2->name(); //base::print(), compile time e) br1.print(); //base::print(), run time f) br2.print(); //derived::print(), run time ==ex.15.15== >Exercise 15.15: Define your own versions of Disc_quote and Bulk_quote. CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_15.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_15.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_15_test.cc|test file]] ==ex.15.16== >Exercise 15.16: Rewrite the class representing a limited discount strategy, which you wrote for the exercises in § 15.2.2 (p. 601), to inherit from Disc_quote. CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_16.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_16.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_16_test.cc|test file]] ==ex.15.17== >Exercise 15.17: Try to define an object of type Disc_quote and see what errors you get from the compiler. ex_15_16_test.cc: In function ‘int main(int, const char**)’: ex_15_16_test.cc:20:13: error: cannot declare variable ‘dq’ to be of abstract type ‘Disc_quote’ 20 | Disc_quote dq; ==ex.15.18== >Exercise 15.18: Given the classes from page 612 and page 613, and assuming each object has the type specified in the comments, determine which of these assignments are legal. Explain why those that are illegal aren’t allowed: // d1 has type Pub_Derv, legal, public inheritance, derived-to-base conversion preformed by user is allowed. Base *p = &d1; // d2 has type Priv_Derv, illegal, private inheritance, derived-to-base conversion preformed by user is not allowed. p = &d2; // d3 has type Prot_Derv, illegal, protected inheritance, only member and friends of the class are allowed to preform derived-to-base conversion. p = &d3; // dd1 has type Derived_from_Public, legal, same reason as the d1 p = &dd1; // dd2 has type Derived_from_Private, illegal, user prefromed derived-to-base conversion is not allowed either in private or proteceted inheritance p = &dd2; // dd3 has type Derived_from_Protected, illegal, user prefromed derived-to-base conversion is not allowed either in private or proteceted inheritance p = &dd3; ==ex.15.19== >Exercise 15.19: Assume that each of the classes from page 612 and page 613 has a member function of the form: void memfcn(Base &b) { b = *this; } Assuming ''D'' inherits from ''B'': * ''Pub_Derv'', //**legal**//, Members and friends of ''D'' can preform the derived-to-base conversion **regardless of how derived class** inherits from the ''B''. * ''Priv_Derv'', //**legal**//, the same resaon as the ''Pub_derv'' * ''Prot_Derv'', //**legal**//, the same resaon as the ''Pub_derv'' * ''Derived_from_Public'', //**legal**//, the derived class of ''D'' may preform the derived-to-base conversion if ''D'' inherits from ''B'' either **public** or **protected**. * ''Derived_from_Private'', //**illegal**//, if ''D'' inherits privately from ''B'', such code may not use the conversion. * ''Derived_from_Protected'', //**legal**//, the same reason as ''Derived_from_Public''. ==ex.15.20== Exercise 15.20: Write code to test your answers to the previous two exercises. ###Test in ex.15.18 #p = ∏ #ex_15_20.cc:23:7: error: ‘Base’ is an inaccessible base of ‘Prot_Derv’ #p = &prid; #ex_15_20.cc:25:7: error: ‘Base’ is an inaccessible base of ‘Priv_Derv’ #p = &dfprod; #ex_15_20.cc:31:7: error: ‘Base’ is an inaccessible base of ‘Derived_from_protected’ #p = &dfprid; #ex_15_20.cc:32:7: error: ‘Base’ is an inaccessible base of ‘Derived_from_private’ ###Test in ex.15.19 #ex_15_20.h: In member function ‘void Derived_from_private::memfcn(Base&)’: #ex_15_20.h:58:30: error: ‘Base’ is an inaccessible base of ‘Derived_from_private’ CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_20.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_20.cc|test file]] ====Ex.15.21-15.30==== ==ex.15.21== >Exercise 15.21: Choose one of the following general abstractions containing a family of types (or choose one of your own). Organize the types into an inheritance hierarchy: * a) Graphical file formats (such as gif, tiff, jpeg, bmp) * b) Geometric primitives (such as box, circle, sphere, cone) * c) C++ language types (such as class, function, member function)
CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_21.h|header]] ==ex.15.22== >Exercise 15.22: For the class you chose in the previous exercise, identify some of the likely virtual functions as well as public and protected members. CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_22.h|header]] ==ex.15.23== >Exercise 15.23: Assuming class D1 on page 620 had intended to override its inherited fcn function, how would you fix that class? Assuming you fixed the class so that fcn matched the definition in Base, how would the calls in that section be resolved? CODE : [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_23.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_23.cc|test]] Please check the comments in the test file to see how the calls will be resolved.\\ \\ Alternative way: using ''using Base::fcn'' to make ''Base::fcn'' avaliable to ''D1'',then overwrite it: class D1 : public Base { public: using Base::fcn; int fcn() override { //...// }; int fcn(int); // parameter list differs from fcn in Base virtual void f2(); // new virtual function that does not exist in Base }; ==ex.15.24== >Exercise 15.24: What kinds of classes need a virtual destructor? What operations must a virtual destructor perform? //A base class almost always needs a destructor, so that it can make the destructor virtual.// There may be cases in which we need to use virtual destructors when derived classes have dynamically allocated resources. ==ex.15.25== >Exercise 15.25: Why did we define a default constructor for Disc_quote? What effect, if any, would removing that constructor have on the behavior of Bulk_quote? This how we define ''Disc_quote'': class Disc_quote : public Quote { public: Disc_quote() = default; Disc_quote(const std::string& book, double price, std::size_t qty, double disc): Quote(book, price), quantity(qty), discount(disc) { } double net_price(std::size_t) const = 0; protected: std::size_t quantity = 0; // purchase size for the discount to apply double discount = 0.0; // fractional discount to apply }; Because ''Disc_quote()'' also defines a constructor that takes four arguments, the compiler will not synthesize a default constructor for the class. Therefore, if we didn't define a default constructor in the ''Disc_quote'', the ''Bluk_quote'' class won't be able to initialize its object since the default constructor in its direct base class isn't accessible. ==ex.15.26== >Exercise 15.26: Define the Quote and Bulk_quote copy-control members to do the same job as the synthesized versions. Give them and the other constructors print statements that identify which function is running. Write programs using these classes and predict what objects will be created and destroyed. Compare your predictions with the output and continue experimenting until your predictions are reliably correct. [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_26.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_26.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_26_test.cc|test file]] ==ex.15.27== >Exercise 15.27: Redefine your Bulk_quote class to inherit its constructors. [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_27.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_27.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_27_test.cc|test file]] ==ex.15.28== >Exercise 15.28: Define a vector to hold Quote objects but put Bulk_quote objects into that vector. Compute the total net_price of all the elements in the vector. [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_28.cc|source]] \\ \\ output: The total should be: 105 The total after a sliced down: 150 ==ex.15.29== >Exercise 15.29: Repeat your program, but this time store shared_ptrs to objects of type Quote. Explain any discrepancy in the sum generated by the this version and the previous program. If there is no discrepancy, explain why there isn’t one. [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_29.cc|source]] \\ \\ output: The total should be: 105 The total with no sliced down: 105 The smart pointer version works because the derived-to-base conversion is performed when calling the member ''netPrice()''. When we store smart pointers into the vector, their type changes from ''shard_ptr'' to ''shared_ptr''. Polymofisim, however, enables us to access the derived version of the virtual function ''netPrice()'', via reference or pointer to the base object. In this case, even though our smart pointers have their type changed, they still point to these objects of the ''Bulk_quote'' type. When we use these smart pointers to call the ''netPrice()'' member they will be bound to the ''Bulk_quote'' objects. As a result, the member ''netPrice()'' we called from these pointers is actually ''Bulk_quote::netPrice()'', which give us the correct result. ==ex.15.30== >Exercise 15.30: Write your own version of the Basket class and use it to compute prices for the same transactions as you used in the previous exercises. [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_30.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_30.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_30_test.cc|test file]] ====Ex.15.31-15.40==== ==ex.15.31== >Exercise 15.31: Given that s1, s2, s3, and s4 are all strings, determine what objects are created in the following expressions: (1) Query(s1) | Query(s2) & ~ Query(s3); (2) Query(s1) | (Query(s2) & ~ Query(s3)); (3) (Query(s1) & (Query(s2)) | (Query(s3) & Query(s4))); \\
\\ \\ ==ex.15.32== >Exercise 15.32: What happens when an object of type Query is copied, moved, assigned, and destroyed? Query class manages objects with Query_base type via smart_pointer. Whenever we perform a copy control behavior over a Query-type object, we are modifying the reference counts of the smart pointer to which the object is bound. Therefore, depending on which operation is performed on the Query object, the reference counts of the smart pointer to which the object is pointed may go up, down, or even to zero and causing the Query_base object to be freed. ==ex.15.33== >Exercise 15.33: What about objects of type Query_base? Since Query_base is an abstract class, there is no concrete object that can be constructed from it. Query_base's derived class objects are managed by the synthesized copy control members. ==ex.15.34== >Exercise 15.34: For the expression built in Figure 15.3 (p. 638): (a) List the constructors executed in processing that expression. (b) List the calls to rep that are made from cout << q. (c) List the calls to eval made from q.eval(). a): \\ \\
\\ \\ b): \\
\\ \\ b): \\
\\ \\ ==ex.15.35== >Exercise 15.35: Implement the Query and Query_base classes, including a definition of rep but omitting the definition of eval. [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_35.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_35.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_35_test.cc|test file]] ==ex.15.36== >Exercise 15.36: Put print statements in the constructors and rep members and run your code to check your answers to (a) and (b) from the first exercise. /* output of statement a */ WordQuery CSTR Query CSTR WordQuery CSTR Query CSTR WordQuery CSTR Query CSTR Query Copy CSTR Query Copy CSTR BinaryQuery CSTR AndQuery CSTR Query shard_ptr CSTR Query Copy CSTR Query Copy CSTR BinaryQuery CSTR OrQuery CSTR Query shard_ptr CSTR /* output of statement b */ Query::rep() BinaryQuery::rep() Query::rep() WordQuery::rep() Query::rep() BinaryQuery::rep() Query::rep() WordQuery::rep() Query::rep() WordQuery::rep() ==ex.15.37== >Exercise 15.37: What changes would your classes need if the derived classes had members of type ''shared_ptr'' rather than of type ''Query''? A reason for storing ''Shared_ptr'' smart pointers in the ''Query'' class is that it facilitates classes management. For example, relationship operations only need an interface to access the resource, and the dynamic binding decides which concrete method is needed. In this program, we can see how the ''&'' / ''|'' / ''~'' operations are advantageous. By letting the derived classes hold a ''shard_ptr'' smart pointer instead (supposing each concrete derived class stores its own smart pointer), the ''Query'' class cannot choose the right class type for us anymore. In other words, we have to manually raise the concrete call and implement overloaded versions for each concrete class. As a result, The whole process will be complexed, not just in querying, but also in implementing. For instance, the equivalent querying of the following example would be like: /* using Query as an interface */ Query(s1) & Query(s2) | Query(s3); /* equivalent if we storing the smart pointer in each derived classes */ WordQuery(s1) & WordQuery(s2) | WordQuery(s3); Besides, the ''|'' operation must define a version that takes ''AndQuery'' and ''WordQuery'' parameters, in order to process the call above: operator&(const WordQuery& lhs, const WordQuery& rhs); //returns a smart pointer, points to an AndQuery Object operator|(const AndQuery & lhs, const WordQuery & rhs); // returns a smart pointer, points to an OrQuery Object ==ex.15.38== >Are the following declarations legal? If not, why not? If so, explain what the declarations mean. // illegal. BinaryQuery is an abstract class that cannot be instanced. BinaryQuery a = Query("fiery") & Query("bird"); /* The constructors in the following classes cannot be called by users since they are private. */ // illegal. the ''&'' operation will returns an smart pointer that manage a AndQuery object; However, AndQuery do not have any constructor that takes a smart pointer as the parameter. AndQuery b = Query("fiery") & Query("bird"); //illegal. same reason as the above OrQuery c = Query("fiery") & Query("bird"); ==ex.15.39== >Exercise 15.39: Implement the Query and Query_base classes. Test your application by evaluating and printing a query such as the one in Figure 15.3 (p. 638). [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_39.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_39.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_39_test.cc|test file]] ==ex.15.40== >Exercise 15.40: In the OrQuery eval function what would happen if its rhs member returned an empty set? What if its lhs member did so? What if both rhs and lhs returned empty sets? ''OrQuery::eval()'' uses ''set::inerst()'' to implement set union operation. Thus, regardless of which operand returns an empty line number set, the final result will be determined by the other. In the event that both return an empty number set, no search result will be returned. ====Ex.15.41-15.42==== ==ex.15.41== > Exercise 15.41: Reimplement your classes to use built-in pointers to Query_base rather than shared_ptrs. Remember that your classes will no longer be able to use the synthesized copy-control members. [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_41.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_41.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_41_test.cc|test file]] ==ex.15.42== >Exercise 15.42: Design and implement one of the following enhancements: >a) Print words only once per sentence rather than once per line. >b) Introduce a history system in which the user can refer to a previous query by number, possibly adding to it or combining it with another. >c) Allow the user to limit the results so that only matches in a given range of lines are displayed. //**(a) Print words only once per sentence rather than once per line.**//\\ [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_42_a.h|header]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_42_a.cc|source]] [[https://github.com/CodingHare/Book_Solutions/blob/master/CppPrimer_5th/ch15/ex_15_42_a_test.cc|test file]] Some thoughts about the other two questions: * b) a new class that store the history records is not hard to implement; a vector should handle it well. The main problem is how to interpret the input string and rewrite it to commands. That may need a phaser; will come back later to do it. * c) we can limited the output range of the line number to get the result.